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"};
219 auto const acctReserve = env.current()->fees().accountReserve(0);
220 auto const incReserve = env.current()->fees().increment;
221 env.fund(acctReserve, alice, minter);
223 BEAST_EXPECT(env.balance(alice) == acctReserve);
224 BEAST_EXPECT(env.balance(minter) == acctReserve);
237 env(pay(env.master, alice, incReserve + drops(9)));
242 auto checkAliceOwnerMintedBurned = [&env,
this, &alice](
255 ss <<
"Wrong " << type <<
" count. Found: " << found
256 <<
"; Expected: " << exp;
257 fail(ss.
str(), __FILE__, line);
260 oneCheck(
"owner",
ownerCount(env, alice), owners);
261 oneCheck(
"minted",
mintedCount(env, alice), minted);
262 oneCheck(
"burned",
burnedCount(env, alice), burned);
269 checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
272 env(pay(env.master, alice, drops(11)));
276 env(token::mint(alice));
278 checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
282 for (
int i = 1; i < 32; ++i)
284 env(token::mint(alice));
285 checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
292 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
295 env(pay(env.master, alice, XRP(50) + drops(329)));
302 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
305 env(pay(env.master, alice, drops(11)));
309 env(token::mint(alice));
311 checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
318 env(token::burn(alice, token::getID(env, alice, 0, seq++)));
320 checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
324 env(token::burn(alice, token::getID(env, alice, 197, 5)),
327 checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
332 env(token::setMinter(alice, minter));
339 auto checkMintersOwnerMintedBurned = [&env,
this, &alice, &minter](
347 auto oneCheck = [
this](
357 ss <<
"Wrong " << type <<
" count. Found: " << found
358 <<
"; Expected: " << exp;
359 fail(ss.
str(), __FILE__, line);
362 oneCheck(
"alice owner",
ownerCount(env, alice), aliceOwners, line);
364 "alice minted",
mintedCount(env, alice), aliceMinted, line);
366 "alice burned",
burnedCount(env, alice), aliceBurned, line);
368 "minter owner",
ownerCount(env, minter), minterOwners, line);
370 "minter minted",
mintedCount(env, minter), minterMinted, line);
372 "minter burned",
burnedCount(env, minter), minterBurned, line);
378 env(pay(env.master, minter, XRP(50) - drops(1)));
380 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
385 env(token::mint(minter),
386 token::issuer(alice),
390 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
393 env(pay(env.master, minter, drops(11)));
397 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
399 checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
403 for (
int i = 1; i < 32; ++i)
405 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
406 checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
411 env(pay(env.master, minter, XRP(50) + drops(319)));
416 env(token::mint(minter),
417 token::issuer(alice),
421 checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
424 env(pay(env.master, minter, drops(11)));
428 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
430 checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
435 env(token::burn(minter, token::getID(env, alice, 0, nftSeq++)));
437 checkMintersOwnerMintedBurned(
438 0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
443 env(token::burn(minter, token::getID(env, alice, 0, nftSeq++)));
445 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
448 env(token::burn(minter, token::getID(env, alice, 2009, 3)),
451 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
459 testcase(
"Mint max tokens");
461 using namespace test::jtx;
463 Account
const alice{
"alice"};
464 Env env{*
this, features};
465 env.fund(XRP(1000), alice);
474 uint256 const nftId0{token::getNextID(env, alice, 0u)};
475 env(token::mint(alice, 0u));
478 env(token::burn(alice, nftId0));
485 env.app().openLedger().modify(
494 auto replacement = std::make_shared<SLE>(*sle, sle->key());
507 (*replacement)[sfMintedNFTokens] = 0x0000'0000;
515 view.rawReplace(replacement);
519 // See whether alice is at the boundary that causes an error.
520 env(token::mint(alice, 0u), ter(tesSUCCESS));
521 env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
525 testMintInvalid(FeatureBitset features)
527 // Explore many of the invalid ways to mint an NFT.
528 testcase("Mint invalid");
530 using namespace test::jtx;
532 Env env{*this, features};
533 Account const alice{"alice"};
534 Account const minter{"minter"};
536 // Fund alice and minter enough to exist, but not enough to meet
537 // the reserve for creating their first NFT. Account reserve for unit
538 // tests is 200 XRP, not 20.
539 env.fund(XRP(200), alice, minter);
542 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
545 // Fund alice enough to start minting NFTs.
546 env(pay(env.master, alice, XRP(1000)));
549 //----------------------------------------------------------------------
552 // Set a negative fee.
553 env(token::mint(alice, 0u),
554 fee(STAmount(10ull, true)),
557 // Set an invalid flag.
558 env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
560 // Can't
set a transfer fee
if the NFT does not have the tfTRANSFERABLE
562 env(token::mint(alice, 0u),
567 env(token::mint(alice, 0u),
573 env(token::mint(alice, 0u), token::issuer(alice), ter(
temMALFORMED));
576 env(token::mint(alice, 0u), token::uri(
""), ter(
temMALFORMED));
579 env(token::mint(alice, 0u),
587 env(token::mint(alice, 0u),
588 token::issuer(Account(
"demon")),
595 env(token::mint(minter, 0u),
596 token::issuer(alice),
604 testcase(
"Burn invalid");
606 using namespace test::jtx;
608 Env env{*
this, features};
609 Account
const alice{
"alice"};
610 Account
const buyer{
"buyer"};
611 Account
const minter{
"minter"};
612 Account
const gw(
"gw");
613 IOU
const gwAUD(gw[
"AUD"]);
618 env.fund(XRP(250), alice, buyer, minter, gw);
632 env(token::burn(alice, nftAlice0ID),
639 env(token::burn(alice, nftAlice0ID),
649 env(token::burn(alice, token::getID(env, alice, 0, 1)),
664 testcase(
"Invalid NFT offer create");
666 using namespace test::jtx;
668 Env env{*
this, features};
669 Account
const alice{
"alice"};
670 Account
const buyer{
"buyer"};
671 Account
const gw(
"gw");
672 IOU
const gwAUD(gw[
"AUD"]);
677 env.fund(XRP(250), alice, buyer, gw);
683 env(token::mint(alice, 0u),
695 uint256 nftNoXferID = token::getNextID(env, alice, 0);
696 env(token::mint(alice, 0));
709 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
716 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
723 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
730 env(token::createOffer(buyer, nftXrpOnlyID, buyer[
"USD"](1)),
732 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](0)),
734 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
740 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](1)),
741 token::expiration(0),
748 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
754 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
762 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
769 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
770 token::destination(alice),
777 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
778 token::destination(Account(
"demon")),
788 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
796 env(token::createOffer(
797 buyer, token::getID(env, alice, 0, 1), XRP(1000)),
804 env(token::createOffer(
805 alice, token::getID(env, alice, 0, 1), XRP(1000)),
812 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
818 env(trust(buyer, gwAUD(1000)));
824 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
836 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
847 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
857 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
866 env(trust(buyer, gwAUD(1000)));
869 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
879 env(pay(gw, buyer, gwAUD(999)));
884 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
891 env(pay(env.master, buyer, XRP(50) + drops(119)));
894 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
901 env(pay(env.master, buyer, drops(11)));
906 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
916 testcase(
"Invalid NFT offer cancel");
918 using namespace test::jtx;
920 Env env{*
this, features};
921 Account
const alice{
"alice"};
922 Account
const buyer{
"buyer"};
923 Account
const gw(
"gw");
924 IOU
const gwAUD(gw[
"AUD"]);
926 env.fund(XRP(1000), alice, buyer, gw);
937 uint256 const buyerOfferIndex =
939 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
949 env(token::cancelOffer(buyer, {buyerOfferIndex}),
956 env(token::cancelOffer(buyer, {buyerOfferIndex}),
976 env(token::cancelOffer(buyer, offers), ter(
temMALFORMED));
982 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
996 env(pay(env.master, gw, XRP(5000)));
1000 env(offer(gw, XRP(i), gwAUD(1)));
1007 env(check::create(gw, env.master, XRP(300)));
1014 env(check::cancel(gw, gwCheckId));
1028 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1036 testcase(
"Invalid NFT offer accept");
1038 using namespace test::jtx;
1040 Env env{*
this, features};
1041 Account
const alice{
"alice"};
1042 Account
const buyer{
"buyer"};
1043 Account
const gw(
"gw");
1044 IOU
const gwAUD(gw[
"AUD"]);
1046 env.fund(XRP(1000), alice, buyer, gw);
1062 uint256 nftNoXferID = token::getNextID(env, alice, 0);
1063 env(token::mint(alice, 0));
1068 uint256 const plainOfferIndex =
1070 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1077 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1082 uint256 const xrpOnlyOfferIndex =
1084 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1089 uint256 const noXferOfferIndex =
1091 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1097 uint256 const aliceExpOfferIndex =
1099 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1109 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1116 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1117 txflags(0x00008000),
1124 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1133 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1143 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1152 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1153 token::brokerFee(gwAUD(0)),
1162 env(token::acceptBuyOffer(buyer, beast::zero),
1169 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1175 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1180 env(token::acceptSellOffer(buyer, beast::zero),
1186 env(token::acceptSellOffer(buyer, missingOfferIndex),
1192 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1201 env(trust(alice, gwAUD(1000)));
1202 env(trust(buyer, gwAUD(1000)));
1204 env(pay(gw, buyer, gwAUD(30)));
1213 uint256 const buyerOfferIndex =
1215 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1216 token::owner(alice));
1221 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1227 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1234 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1240 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1246 uint256 const buyerOfferIndex =
1248 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1249 token::owner(alice));
1255 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1256 token::brokerFee(XRP(40)),
1262 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1263 token::brokerFee(gwAUD(31)),
1270 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1271 token::brokerFee(gwAUD(1.5)),
1277 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1285 uint256 const buyerOfferIndex =
1287 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1288 token::owner(alice));
1293 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1299 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1305 env(pay(buyer, gw, gwAUD(30)));
1307 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1308 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1317 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1320 env(token::acceptSellOffer(gw, offerIndex));
1324 env(pay(gw, buyer, gwAUD(30)));
1328 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1334 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1342 uint256 const buyerOfferIndex =
1344 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1345 token::owner(alice));
1350 env(token::acceptSellOffer(alice, buyerOfferIndex),
1356 env(token::acceptSellOffer(alice, plainOfferIndex),
1363 env(token::acceptSellOffer(buyer, plainOfferIndex),
1374 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1377 env(token::acceptSellOffer(alice, offerIndex));
1381 env(pay(buyer, gw, gwAUD(30)));
1383 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1384 env(token::acceptSellOffer(buyer, audOfferIndex),
1401 testcase(
"Mint flagBurnable");
1403 using namespace test::jtx;
1405 Env env{*
this, features};
1406 Account
const alice{
"alice"};
1407 Account
const buyer{
"buyer"};
1408 Account
const minter1{
"minter1"};
1409 Account
const minter2{
"minter2"};
1411 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1416 env(token::setMinter(alice, minter1));
1423 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1425 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1426 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1431 env(token::createOffer(minter1, nftID, XRP(0)),
1435 env(token::acceptSellOffer(buyer, offerIndex));
1443 uint256 const noBurnID = nftToBuyer(0);
1444 env(token::burn(alice, noBurnID),
1445 token::owner(buyer),
1448 env(token::burn(minter1, noBurnID),
1449 token::owner(buyer),
1452 env(token::burn(minter2, noBurnID),
1453 token::owner(buyer),
1458 env(token::burn(buyer, noBurnID), token::owner(buyer));
1465 env(token::burn(minter2, burnableID),
1466 token::owner(buyer),
1471 env(token::burn(alice, burnableID), token::owner(buyer));
1479 env(token::burn(buyer, burnableID));
1487 env(token::burn(buyer, burnableID), token::owner(buyer));
1497 env(token::setMinter(alice, minter2));
1502 env(token::burn(minter1, burnableID),
1503 token::owner(buyer),
1509 env(token::burn(minter2, burnableID), token::owner(buyer));
1519 testcase(
"Mint flagOnlyXRP");
1521 using namespace test::jtx;
1523 Env env{*
this, features};
1524 Account
const alice{
"alice"};
1525 Account
const buyer{
"buyer"};
1526 Account
const gw(
"gw");
1527 IOU
const gwAUD(gw[
"AUD"]);
1530 env.fund(XRP(1000), alice, buyer, gw);
1532 env(trust(alice, gwAUD(1000)));
1533 env(trust(buyer, gwAUD(1000)));
1535 env(pay(gw, buyer, gwAUD(100)));
1545 uint256 const aliceOfferIndex =
1547 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1553 uint256 const buyerOfferIndex =
1555 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1556 token::owner(alice));
1561 env(token::cancelOffer(alice, {aliceOfferIndex}));
1562 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1568 env(token::burn(alice, nftIOUsOkayID));
1581 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1588 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1589 token::owner(alice),
1596 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1602 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1603 token::owner(alice));
1613 testcase(
"Mint flagCreateTrustLines");
1615 using namespace test::jtx;
1617 Account
const alice{
"alice"};
1618 Account
const becky{
"becky"};
1619 Account
const cheri{
"cheri"};
1620 Account
const gw(
"gw");
1621 IOU
const gwAUD(gw[
"AUD"]);
1622 IOU
const gwCAD(gw[
"CAD"]);
1623 IOU
const gwEUR(gw[
"EUR"]);
1628 for (
auto const& tweakedFeatures :
1632 Env env{*
this, tweakedFeatures};
1633 env.fund(XRP(1000), alice, becky, cheri, gw);
1637 env(trust(becky, gwAUD(1000)));
1638 env(trust(cheri, gwAUD(1000)));
1639 env(trust(becky, gwCAD(1000)));
1640 env(trust(cheri, gwCAD(1000)));
1641 env(trust(becky, gwEUR(1000)));
1642 env(trust(cheri, gwEUR(1000)));
1644 env(pay(gw, becky, gwAUD(500)));
1645 env(pay(gw, becky, gwCAD(500)));
1646 env(pay(gw, becky, gwEUR(500)));
1647 env(pay(gw, cheri, gwAUD(500)));
1648 env(pay(gw, cheri, gwCAD(500)));
1655 uint256 const nftNoAutoTrustID{
1657 env(token::mint(alice, 0u),
1658 token::xferFee(xferFee),
1663 uint256 const beckyBuyOfferIndex =
1665 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1666 token::owner(alice));
1668 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1672 TER const createOfferTER =
1674 uint256 const beckyOfferIndex =
1676 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1678 ter(createOfferTER));
1682 uint256 const cheriOfferIndex =
1684 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1685 token::owner(becky),
1686 ter(createOfferTER));
1690 env(token::cancelOffer(becky, {beckyOfferIndex}));
1691 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1699 uint256 const nftAutoTrustID{token::getNextID(
1710 env(token::mint(alice, 0u),
1711 token::xferFee(transferFee),
1722 uint256 const beckyBuyOfferIndex =
1724 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1725 token::owner(alice));
1727 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1731 uint256 const beckySellOfferIndex =
1733 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1736 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1740 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1743 uint256 const beckyBuyBackOfferIndex =
1745 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1746 token::owner(cheri));
1748 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1752 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1753 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1759 uint256 const nftNoAutoTrustID{token::getNextID(
1761 env(token::mint(alice, 0u),
1762 token::xferFee(transferFee),
1767 uint256 const aliceSellOfferIndex =
1769 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1772 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1778 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1781 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1785 uint256 const cheriSellOfferIndex =
1787 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1790 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1796 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1805 testcase(
"Mint flagTransferable");
1807 using namespace test::jtx;
1809 Env env{*
this, features};
1811 Account
const alice{
"alice"};
1812 Account
const becky{
"becky"};
1813 Account
const minter{
"minter"};
1815 env.fund(XRP(1000), alice, becky, minter);
1821 uint256 const nftAliceNoTransferID{
1822 token::getNextID(env, alice, 0u)};
1823 env(token::mint(alice, 0u), token::xferFee(0));
1829 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1830 token::owner(alice),
1834 uint256 const aliceSellOfferIndex =
1836 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1839 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1845 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1854 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1856 token::destination(alice),
1864 uint256 const aliceBuyOfferIndex =
1866 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1867 token::owner(becky));
1869 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1875 env(token::burn(alice, nftAliceNoTransferID));
1882 env(token::setMinter(alice, minter));
1886 uint256 const nftMinterNoTransferID{
1887 token::getNextID(env, alice, 0u)};
1888 env(token::mint(minter), token::issuer(alice));
1894 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1895 token::owner(minter),
1901 env(token::clearMinter(alice));
1906 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1914 for (
int i = 0; i < 10; ++i)
1917 env(token::setMinter(alice, minter));
1923 uint256 const minterSellOfferIndex =
1925 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1932 env(token::clearMinter(alice));
1938 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1944 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1952 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1953 token::owner(becky),
1960 uint256 const aliceBuyOfferIndex =
1962 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1963 token::owner(becky));
1969 for (
int i = 0; i < 10; ++i)
1972 env(token::setMinter(alice, minter));
1977 uint256 const minterBuyOfferIndex =
1979 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1980 token::owner(becky));
1986 env(token::clearMinter(alice));
1992 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
2000 env(token::burn(minter, nftMinterNoTransferID), ter(
tesSUCCESS));
2002 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
2019 uint256 const aliceSellOfferIndex =
2021 env(token::createOffer(alice, nftAliceID, XRP(20)),
2026 uint256 const beckyBuyOfferIndex =
2028 env(token::createOffer(becky, nftAliceID, XRP(21)),
2029 token::owner(alice));
2034 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
2040 uint256 const beckySellOfferIndex =
2042 env(token::createOffer(becky, nftAliceID, XRP(22)),
2050 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2057 uint256 const minterSellOfferIndex =
2059 env(token::createOffer(minter, nftAliceID, XRP(23)),
2067 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2075 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2083 env(token::burn(becky, nftAliceID));
2095 testcase(
"Mint transferFee");
2097 using namespace test::jtx;
2099 Env env{*
this, features};
2101 Account
const alice{
"alice"};
2102 Account
const becky{
"becky"};
2103 Account
const carol{
"carol"};
2104 Account
const minter{
"minter"};
2105 Account
const gw{
"gw"};
2106 IOU
const gwXAU(gw[
"XAU"]);
2108 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2111 env(trust(alice, gwXAU(2000)));
2112 env(trust(becky, gwXAU(2000)));
2113 env(trust(carol, gwXAU(2000)));
2114 env(trust(minter, gwXAU(2000)));
2116 env(pay(gw, alice, gwXAU(1000)));
2117 env(pay(gw, becky, gwXAU(1000)));
2118 env(pay(gw, carol, gwXAU(1000)));
2119 env(pay(gw, minter, gwXAU(1000)));
2124 env(token::setMinter(alice, minter));
2141 uint256 const beckyBuyOfferIndex =
2143 env(token::createOffer(becky, nftID, gwXAU(10)),
2144 token::owner(alice));
2146 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2147 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2149 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2151 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2152 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2155 uint256 const beckySellOfferIndex =
2157 env(token::createOffer(becky, nftID, gwXAU(10)),
2160 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2162 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2163 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2164 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2167 uint256 const minterBuyOfferIndex =
2169 env(token::createOffer(minter, nftID, gwXAU(10)),
2170 token::owner(carol));
2172 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2174 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2175 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2176 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2177 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2181 uint256 const minterSellOfferIndex =
2183 env(token::createOffer(minter, nftID, gwXAU(10)),
2186 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2188 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2189 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2190 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2191 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2194 env(token::burn(alice, nftID));
2207 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2211 uint256 const beckyBuyOfferIndex =
2213 env(token::createOffer(becky, nftID, gwXAU(10)),
2214 token::owner(alice));
2216 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2217 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2219 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2221 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2222 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2225 uint256 const beckySellOfferIndex =
2227 env(token::createOffer(becky, nftID, gwXAU(10)),
2230 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2233 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2234 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2235 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2238 uint256 const minterBuyOfferIndex =
2240 env(token::createOffer(minter, nftID, gwXAU(10)),
2241 token::owner(carol));
2243 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2246 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2247 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2248 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2249 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2253 uint256 const minterSellOfferIndex =
2255 env(token::createOffer(minter, nftID, gwXAU(10)),
2258 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2260 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2261 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2262 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2263 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2267 env(pay(alice, becky, gwXAU(0.0001)));
2268 env(pay(alice, carol, gwXAU(0.0001)));
2271 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2272 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2273 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2274 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2277 env(token::burn(alice, nftID));
2288 env(token::mint(alice),
2295 uint256 const nftID = token::getNextID(
2297 env(token::mint(alice),
2303 uint256 const beckyBuyOfferIndex =
2305 env(token::createOffer(becky, nftID, gwXAU(10)),
2306 token::owner(alice));
2308 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2309 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2311 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2313 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2314 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2317 uint256 const beckySellOfferIndex =
2319 env(token::createOffer(becky, nftID, gwXAU(100)),
2322 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2325 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2326 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2327 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2330 uint256 const carolBuyOfferIndex =
2332 env(token::createOffer(carol, nftID, gwXAU(10)),
2333 token::owner(minter));
2335 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2338 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2339 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2340 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2341 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2345 uint256 const carolSellOfferIndex =
2347 env(token::createOffer(carol, nftID, gwXAU(10)),
2350 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2353 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2354 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2355 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2356 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2359 env(pay(alice, minter, gwXAU(55)));
2360 env(pay(becky, minter, gwXAU(40)));
2362 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2363 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2364 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2365 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2368 env(token::burn(alice, nftID));
2378 for (
auto NumberSwitchOver : {
true})
2380 if (NumberSwitchOver)
2388 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2394 STAmount aliceBalance = env.balance(alice);
2395 STAmount minterBalance = env.balance(minter);
2396 uint256 const minterBuyOfferIndex =
2398 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2400 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2402 aliceBalance += XRP(1) - fee;
2403 minterBalance -= XRP(1) + fee;
2404 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2405 BEAST_EXPECT(env.balance(minter) == minterBalance);
2409 auto pmt = NumberSwitchOver ? drops(50000) : drops(99999);
2410 STAmount carolBalance = env.balance(carol);
2411 uint256 const minterSellOfferIndex =
2413 env(token::createOffer(minter, nftID, pmt), txflags(
tfSellNFToken));
2415 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2417 minterBalance += pmt - fee;
2418 carolBalance -= pmt + fee;
2419 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2420 BEAST_EXPECT(env.balance(minter) == minterBalance);
2421 BEAST_EXPECT(env.balance(carol) == carolBalance);
2425 STAmount beckyBalance = env.balance(becky);
2426 uint256 const beckyBuyOfferIndex =
2428 pmt = NumberSwitchOver ? drops(50001) : drops(100000);
2429 env(token::createOffer(becky, nftID, pmt), token::owner(carol));
2431 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2433 carolBalance += pmt - drops(1) - fee;
2434 beckyBalance -= pmt + fee;
2435 aliceBalance += drops(1);
2437 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2438 BEAST_EXPECT(env.balance(minter) == minterBalance);
2439 BEAST_EXPECT(env.balance(carol) == carolBalance);
2440 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2449 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2455 env(pay(alice, gw, env.balance(alice, gwXAU)));
2456 env(pay(minter, gw, env.balance(minter, gwXAU)));
2457 env(pay(becky, gw, env.balance(becky, gwXAU)));
2462 env(pay(gw, alice, startXAUBalance));
2463 env(pay(gw, minter, startXAUBalance));
2464 env(pay(gw, becky, startXAUBalance));
2473 STAmount aliceBalance = env.balance(alice, gwXAU);
2474 STAmount minterBalance = env.balance(minter, gwXAU);
2475 uint256 const minterBuyOfferIndex =
2477 env(token::createOffer(minter, nftID, tinyXAU),
2478 token::owner(alice));
2480 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2482 aliceBalance += tinyXAU;
2483 minterBalance -= tinyXAU;
2484 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2485 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2488 STAmount carolBalance = env.balance(carol, gwXAU);
2489 uint256 const minterSellOfferIndex =
2491 env(token::createOffer(minter, nftID, tinyXAU),
2494 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2497 minterBalance += tinyXAU;
2498 carolBalance -= tinyXAU;
2500 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2501 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2502 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2509 STAmount beckyBalance = env.balance(becky, gwXAU);
2510 uint256 const beckyBuyOfferIndex =
2512 env(token::createOffer(becky, nftID, cheapNFT),
2513 token::owner(carol));
2515 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2518 aliceBalance += tinyXAU;
2519 beckyBalance -= cheapNFT;
2520 carolBalance += cheapNFT - tinyXAU;
2521 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2522 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2523 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2524 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2532 testcase(
"Mint taxon");
2534 using namespace test::jtx;
2536 Env env{*
this, features};
2538 Account
const alice{
"alice"};
2539 Account
const becky{
"becky"};
2541 env.fund(XRP(1000), alice, becky);
2550 uint256 const nftID = token::getNextID(env, alice, 0u);
2557 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2565 for (
int i = 0; i < 10; ++i)
2575 ss <<
"Taxon recovery failed from nftID "
2576 <<
to_string(nftID) <<
". Expected: " << taxon
2577 <<
"; got: " << gotTaxon;
2582 uint256 const nftAliceID = token::getID(
2586 rand_int<std::uint32_t>(),
2587 rand_int<std::uint16_t>(),
2588 rand_int<std::uint16_t>());
2589 check(taxon, nftAliceID);
2591 uint256 const nftBeckyID = token::getID(
2595 rand_int<std::uint32_t>(),
2596 rand_int<std::uint16_t>(),
2597 rand_int<std::uint16_t>());
2598 check(taxon, nftBeckyID);
2610 testcase(
"Mint URI");
2612 using namespace test::jtx;
2614 Env env{*
this, features};
2616 Account
const alice{
"alice"};
2617 Account
const becky{
"becky"};
2619 env.fund(XRP(10000), alice, becky);
2625 auto randURI = []() {
2647 : uri(std::move(uri_)), taxon(taxon_)
2655 entries.
emplace_back(randURI(), rand_int<std::uint32_t>());
2658 for (Entry
const& entry : entries)
2660 if (entry.uri.empty())
2662 env(token::mint(alice, entry.taxon));
2666 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2674 params[jss::account] = alice.human();
2675 params[jss::type] =
"state";
2676 return env.rpc(
"json",
"account_nfts",
to_string(params));
2680 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2681 if (!BEAST_EXPECT(nfts.
size() == entries.size()))
2694 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2699 Entry
const& entry = entries[i];
2702 if (entry.uri.empty())
2717 testcase(
"Create offer destination");
2719 using namespace test::jtx;
2721 Env env{*
this, features};
2723 Account
const issuer{
"issuer"};
2724 Account
const minter{
"minter"};
2725 Account
const buyer{
"buyer"};
2726 Account
const broker{
"broker"};
2728 env.fund(XRP(1000), issuer, minter, buyer, broker);
2732 env(token::setMinter(issuer, minter));
2737 env(token::mint(minter, 0),
2738 token::issuer(issuer),
2745 uint256 const offerMinterToIssuer =
2747 env(token::createOffer(minter, nftokenID, drops(1)),
2748 token::destination(issuer),
2751 uint256 const offerMinterToBuyer =
2753 env(token::createOffer(minter, nftokenID, drops(1)),
2754 token::destination(buyer),
2757 uint256 const offerIssuerToMinter =
2759 env(token::createOffer(issuer, nftokenID, drops(1)),
2760 token::owner(minter),
2761 token::destination(minter));
2763 uint256 const offerIssuerToBuyer =
2765 env(token::createOffer(issuer, nftokenID, drops(1)),
2766 token::owner(minter),
2767 token::destination(buyer));
2781 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2783 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2785 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2787 env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2796 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2797 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2798 env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2799 env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2809 uint256 const offerMinterSellsToBuyer =
2811 env(token::createOffer(minter, nftokenID, drops(1)),
2812 token::destination(buyer),
2821 env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2829 env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2839 uint256 const offerMinterBuysFromBuyer =
2841 env(token::createOffer(minter, nftokenID, drops(1)),
2842 token::owner(buyer),
2843 token::destination(buyer));
2851 env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2859 env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2868 uint256 const offerBuyerBuysFromMinter =
2870 env(token::createOffer(buyer, nftokenID, drops(1)),
2871 token::owner(minter),
2872 token::destination(broker));
2878 env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2883 env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2893 uint256 const offerMinterToBroker =
2895 env(token::createOffer(minter, nftokenID, drops(1)),
2896 token::destination(broker),
2899 uint256 const offerBuyerToMinter =
2901 env(token::createOffer(buyer, nftokenID, drops(1)),
2902 token::owner(minter));
2915 env(token::brokerOffers(
2916 issuer, offerBuyerToMinter, offerMinterToBroker),
2926 env(token::brokerOffers(
2927 broker, offerBuyerToMinter, offerMinterToBroker));
2938 uint256 const offerBuyerToMinter =
2940 env(token::createOffer(buyer, nftokenID, drops(1)),
2941 token::destination(minter),
2944 uint256 const offerMinterToBuyer =
2946 env(token::createOffer(minter, nftokenID, drops(1)),
2947 token::owner(buyer));
2949 uint256 const offerIssuerToBuyer =
2951 env(token::createOffer(issuer, nftokenID, drops(1)),
2952 token::owner(buyer));
2965 env(token::brokerOffers(
2966 broker, offerIssuerToBuyer, offerBuyerToMinter),
2979 env(token::brokerOffers(
2980 broker, offerMinterToBuyer, offerBuyerToMinter),
2986 env(token::acceptBuyOffer(buyer, offerMinterToBuyer));
2990 env(token::cancelOffer(buyer, {offerBuyerToMinter}));
2998 env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
3010 uint256 const offerMinterToBroker =
3012 env(token::createOffer(minter, nftokenID, drops(1)),
3013 token::destination(broker),
3016 uint256 const offerBuyerToBroker =
3018 env(token::createOffer(buyer, nftokenID, drops(1)),
3019 token::owner(minter),
3020 token::destination(broker));
3028 env(token::brokerOffers(
3029 issuer, offerBuyerToBroker, offerMinterToBroker),
3038 env(token::brokerOffers(
3039 broker, offerBuyerToBroker, offerMinterToBroker));
3050 testcase(
"Create offer destination disallow incoming");
3052 using namespace test::jtx;
3057 Account
const alice{
"alice"};
3058 env.fund(XRP(10000), alice);
3061 auto const sle = env.le(alice);
3062 uint32_t flags = sle->getFlags();
3068 Account
const issuer{
"issuer"};
3069 Account
const minter{
"minter"};
3070 Account
const buyer{
"buyer"};
3071 Account
const alice{
"alice"};
3073 env.fund(XRP(1000), issuer, minter, buyer, alice);
3075 env(token::setMinter(issuer, minter));
3080 env(token::mint(minter, 0),
3081 token::issuer(issuer),
3091 env(token::createOffer(minter, nftokenID, drops(1)),
3092 token::destination(buyer),
3110 env(token::createOffer(minter, nftokenID, drops(1)),
3111 token::destination(buyer),
3115 env(token::cancelOffer(minter, {offerIndex}));
3124 env(token::createOffer(minter, nftokenID, drops(1)),
3125 token::destination(buyer),
3132 env(token::cancelOffer(minter, {offerIndex}));
3144 env(token::createOffer(minter, nftokenID, drops(1)),
3145 token::destination(buyer),
3149 env(token::acceptSellOffer(buyer, offerIndex));
3161 env(token::createOffer(alice, nftokenID, drops(1)),
3162 token::owner(buyer),
3169 env(token::createOffer(minter, nftokenID, drops(1)),
3170 token::owner(buyer),
3180 testcase(
"Create offer expiration");
3182 using namespace test::jtx;
3184 Env env{*
this, features};
3186 Account
const issuer{
"issuer"};
3187 Account
const minter{
"minter"};
3188 Account
const buyer{
"buyer"};
3190 env.fund(XRP(1000), issuer, minter, buyer);
3194 env(token::setMinter(issuer, minter));
3199 env(token::mint(minter, 0),
3200 token::issuer(issuer),
3206 env(token::mint(minter, 0),
3207 token::issuer(issuer),
3216 uint256 const offerMinterToIssuer =
3218 env(token::createOffer(minter, nftokenID0, drops(1)),
3219 token::destination(issuer),
3220 token::expiration(expiration),
3223 uint256 const offerMinterToAnyone =
3225 env(token::createOffer(minter, nftokenID0, drops(1)),
3226 token::expiration(expiration),
3229 uint256 const offerIssuerToMinter =
3231 env(token::createOffer(issuer, nftokenID0, drops(1)),
3232 token::owner(minter),
3233 token::expiration(expiration));
3235 uint256 const offerBuyerToMinter =
3237 env(token::createOffer(buyer, nftokenID0, drops(1)),
3238 token::owner(minter),
3239 token::expiration(expiration));
3251 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3253 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3256 BEAST_EXPECT(
lastClose(env) < expiration);
3262 env(token::cancelOffer(minter, {offerMinterToAnyone}));
3266 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3277 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3278 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3293 env(token::createOffer(minter, nftokenID0, drops(1)),
3294 token::expiration(expiration),
3299 env(token::createOffer(minter, nftokenID1, drops(1)),
3300 token::expiration(expiration),
3303 BEAST_EXPECT(
lastClose(env) < expiration);
3309 env(token::acceptSellOffer(buyer, offer0));
3320 env(token::acceptSellOffer(buyer, offer1), ter(
tecEXPIRED));
3321 env(token::acceptSellOffer(issuer, offer1), ter(
tecEXPIRED));
3330 env(token::cancelOffer(issuer, {offer1}));
3340 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3342 token::destination(minter));
3344 env(token::acceptSellOffer(minter, offerSellBack));
3358 env(token::createOffer(buyer, nftokenID0, drops(1)),
3359 token::owner(minter),
3360 token::expiration(expiration));
3363 env(token::createOffer(buyer, nftokenID1, drops(1)),
3364 token::owner(minter),
3365 token::expiration(expiration));
3367 BEAST_EXPECT(
lastClose(env) < expiration);
3373 env(token::acceptBuyOffer(minter, offer0));
3384 env(token::acceptBuyOffer(minter, offer1), ter(
tecEXPIRED));
3385 env(token::acceptBuyOffer(issuer, offer1), ter(
tecEXPIRED));
3394 env(token::cancelOffer(issuer, {offer1}));
3404 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3406 token::destination(minter));
3408 env(token::acceptSellOffer(minter, offerSellBack));
3423 env(token::createOffer(minter, nftokenID0, drops(1)),
3424 token::expiration(expiration),
3429 env(token::createOffer(minter, nftokenID1, drops(1)),
3430 token::expiration(expiration),
3435 env(token::createOffer(buyer, nftokenID0, drops(1)),
3436 token::owner(minter));
3440 env(token::createOffer(buyer, nftokenID1, drops(1)),
3441 token::owner(minter));
3444 BEAST_EXPECT(
lastClose(env) < expiration);
3450 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3461 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3471 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3481 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3483 token::destination(minter));
3485 env(token::acceptSellOffer(minter, offerSellBack));
3500 env(token::createOffer(minter, nftokenID0, drops(1)),
3505 env(token::createOffer(minter, nftokenID1, drops(1)),
3510 env(token::createOffer(buyer, nftokenID0, drops(1)),
3511 token::expiration(expiration),
3512 token::owner(minter));
3516 env(token::createOffer(buyer, nftokenID1, drops(1)),
3517 token::expiration(expiration),
3518 token::owner(minter));
3521 BEAST_EXPECT(
lastClose(env) < expiration);
3527 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3538 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3548 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3558 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3560 token::destination(minter));
3562 env(token::acceptSellOffer(minter, offerSellBack));
3578 env(token::createOffer(minter, nftokenID0, drops(1)),
3579 token::expiration(expiration),
3584 env(token::createOffer(minter, nftokenID1, drops(1)),
3585 token::expiration(expiration),
3590 env(token::createOffer(buyer, nftokenID0, drops(1)),
3591 token::expiration(expiration),
3592 token::owner(minter));
3596 env(token::createOffer(buyer, nftokenID1, drops(1)),
3597 token::expiration(expiration),
3598 token::owner(minter));
3601 BEAST_EXPECT(
lastClose(env) < expiration);
3607 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3618 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3628 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3638 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3640 token::destination(minter));
3642 env(token::acceptSellOffer(minter, offerSellBack));
3654 testcase(
"Cancel offers");
3656 using namespace test::jtx;
3658 Env env{*
this, features};
3660 Account
const alice(
"alice");
3661 Account
const becky(
"becky");
3662 Account
const minter(
"minter");
3663 env.fund(XRP(50000), alice, becky, minter);
3667 env(token::setMinter(alice, minter));
3676 uint256 const expiredOfferIndex =
3679 env(token::createOffer(alice, nftokenID, XRP(1000)),
3681 token::expiration(
lastClose(env) + 13));
3686 env(token::cancelOffer(becky, {expiredOfferIndex}),
3694 env(token::cancelOffer(becky, {expiredOfferIndex}));
3700 uint256 const dest1OfferIndex =
3703 env(token::createOffer(alice, nftokenID, XRP(1000)),
3704 token::destination(becky),
3710 env(token::cancelOffer(minter, {dest1OfferIndex}),
3715 env(token::cancelOffer(becky, {dest1OfferIndex}));
3720 uint256 const dest2OfferIndex =
3723 env(token::createOffer(alice, nftokenID, XRP(1000)),
3724 token::destination(becky),
3729 env(token::cancelOffer(alice, {dest2OfferIndex}));
3736 uint256 const mintersNFTokenID =
3738 env(token::mint(minter, 0),
3739 token::issuer(alice),
3743 uint256 const minterOfferIndex =
3746 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3752 env(token::cancelOffer(alice, {minterOfferIndex}),
3754 env(token::cancelOffer(becky, {minterOfferIndex}),
3759 env(token::cancelOffer(minter, {minterOfferIndex}));
3768 testcase(
"Cancel too many offers");
3770 using namespace test::jtx;
3772 Env env{*
this, features};
3787 Account
const alice(
"alice");
3788 env.fund(XRP(1000), alice);
3797 Account
const offerAcct(
3799 env.fund(XRP(1000), nftAcct, offerAcct);
3804 env(token::mint(nftAcct, 0),
3811 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3812 token::owner(nftAcct),
3821 for (
uint256 const& offerIndex : offerIndexes)
3828 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3832 env(token::cancelOffer(alice, {offerIndexes.back()}));
3837 offerIndexes.pop_back();
3843 env(token::mint(alice, 0),
3849 env(token::createOffer(alice, nftokenID, drops(1)),
3858 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3862 env(token::burn(alice, nftokenID));
3868 offerIndexes.pop_back();
3873 env(token::cancelOffer(alice, offerIndexes));
3877 for (
uint256 const& offerIndex : offerIndexes)
3887 testcase(
"Brokered NFT offer accept");
3889 using namespace test::jtx;
3891 for (
auto const& tweakedFeatures :
3895 Env env{*
this, tweakedFeatures};
3903 Account
const issuer{
"issuer"};
3904 Account
const minter{
"minter"};
3905 Account
const buyer{
"buyer"};
3906 Account
const broker{
"broker"};
3907 Account
const gw{
"gw"};
3908 IOU
const gwXAU(gw[
"XAU"]);
3910 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3913 env(trust(issuer, gwXAU(2000)));
3914 env(trust(minter, gwXAU(2000)));
3915 env(trust(buyer, gwXAU(2000)));
3916 env(trust(broker, gwXAU(2000)));
3919 env(token::setMinter(issuer, minter));
3923 auto checkOwnerCountIsOne =
3928 for (Account
const& acct : accounts)
3935 ss <<
"Account " << acct.human()
3936 <<
" expected ownerCount == 1. Got "
3938 fail(ss.
str(), __FILE__, line);
3944 auto mintNFT = [&env, &issuer, &minter](
std::uint16_t xferFee = 0) {
3947 env(token::mint(minter, 0),
3948 token::issuer(issuer),
3949 token::xferFee(xferFee),
3961 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3963 uint256 const nftID = mintNFT();
3966 uint256 const minterOfferIndex =
3968 env(token::createOffer(minter, nftID, XRP(0)),
3976 env(token::createOffer(buyer, nftID, XRP(1)),
3977 token::owner(minter));
3980 auto const minterBalance = env.balance(minter);
3981 auto const buyerBalance = env.balance(buyer);
3982 auto const brokerBalance = env.balance(broker);
3983 auto const issuerBalance = env.balance(issuer);
3986 env(token::brokerOffers(
3987 broker, buyOfferIndex, minterOfferIndex));
3992 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3993 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3994 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3995 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3998 env(token::burn(buyer, nftID));
4008 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4010 uint256 const nftID = mintNFT();
4013 uint256 const minterOfferIndex =
4015 env(token::createOffer(minter, nftID, XRP(0)),
4023 env(token::createOffer(buyer, nftID, XRP(1)),
4024 token::owner(minter));
4028 env(token::brokerOffers(
4029 broker, buyOfferIndex, minterOfferIndex),
4030 token::brokerFee(XRP(1.1)),
4034 auto const minterBalance = env.balance(minter);
4035 auto const buyerBalance = env.balance(buyer);
4036 auto const brokerBalance = env.balance(broker);
4037 auto const issuerBalance = env.balance(issuer);
4040 env(token::brokerOffers(
4041 broker, buyOfferIndex, minterOfferIndex),
4042 token::brokerFee(XRP(0.5)));
4047 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4048 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4050 env.balance(broker) ==
4051 brokerBalance + XRP(0.5) - drops(10));
4052 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
4055 env(token::burn(buyer, nftID));
4065 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4070 uint256 const minterOfferIndex =
4072 env(token::createOffer(minter, nftID, XRP(0)),
4080 env(token::createOffer(buyer, nftID, XRP(1)),
4081 token::owner(minter));
4084 auto const minterBalance = env.balance(minter);
4085 auto const buyerBalance = env.balance(buyer);
4086 auto const brokerBalance = env.balance(broker);
4087 auto const issuerBalance = env.balance(issuer);
4090 env(token::brokerOffers(
4091 broker, buyOfferIndex, minterOfferIndex));
4096 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4097 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4098 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
4099 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
4102 env(token::burn(buyer, nftID));
4112 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4117 uint256 const minterOfferIndex =
4119 env(token::createOffer(minter, nftID, XRP(0)),
4127 env(token::createOffer(buyer, nftID, XRP(1)),
4128 token::owner(minter));
4131 auto const minterBalance = env.balance(minter);
4132 auto const buyerBalance = env.balance(buyer);
4133 auto const brokerBalance = env.balance(broker);
4134 auto const issuerBalance = env.balance(issuer);
4137 env(token::brokerOffers(
4138 broker, buyOfferIndex, minterOfferIndex),
4139 token::brokerFee(XRP(0.75)));
4145 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
4146 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4148 env.balance(broker) ==
4149 brokerBalance + XRP(0.75) - drops(10));
4150 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
4153 env(token::burn(buyer, nftID));
4159 auto setXAUBalance =
4160 [
this, &gw, &gwXAU, &env](
4165 for (Account
const& acct : accounts)
4167 auto const xauAmt = gwXAU(amount);
4168 auto const balance = env.balance(acct, gwXAU);
4169 if (balance < xauAmt)
4171 env(pay(gw, acct, xauAmt - balance));
4174 else if (balance > xauAmt)
4176 env(pay(acct, gw, balance - xauAmt));
4179 if (env.balance(acct, gwXAU) != xauAmt)
4182 ss <<
"Unable to set " << acct.human()
4183 <<
" account balance to gwXAU(" << amount <<
")";
4184 this->fail(ss.
str(), __FILE__, line);
4192 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4193 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4195 uint256 const nftID = mintNFT();
4198 uint256 const minterOfferIndex =
4200 env(token::createOffer(minter, nftID, gwXAU(1000)),
4209 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4210 token::owner(minter));
4214 env(token::brokerOffers(
4215 broker, buyOfferIndex, minterOfferIndex),
4221 env(token::cancelOffer(buyer, {buyOfferIndex}));
4229 env(token::createOffer(buyer, nftID, gwXAU(999)),
4230 token::owner(minter));
4234 env(token::brokerOffers(
4235 broker, buyOfferIndex, minterOfferIndex),
4241 env(token::cancelOffer(buyer, {buyOfferIndex}));
4248 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4249 token::owner(minter));
4253 env(token::brokerOffers(
4254 broker, buyOfferIndex, minterOfferIndex),
4255 token::brokerFee(gwXAU(0.1)),
4260 env(token::brokerOffers(
4261 broker, buyOfferIndex, minterOfferIndex));
4268 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4269 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4270 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4271 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4274 env(token::burn(buyer, nftID));
4281 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4282 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4287 uint256 const minterOfferIndex =
4289 env(token::createOffer(minter, nftID, gwXAU(900)),
4297 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4298 token::owner(minter));
4302 env(token::brokerOffers(
4303 broker, buyOfferIndex, minterOfferIndex),
4309 env(token::cancelOffer(buyer, {buyOfferIndex}));
4317 env(token::createOffer(buyer, nftID, gwXAU(899)),
4318 token::owner(minter));
4322 env(token::brokerOffers(
4323 broker, buyOfferIndex, minterOfferIndex),
4329 env(token::cancelOffer(buyer, {buyOfferIndex}));
4335 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4336 token::owner(minter));
4341 env(token::brokerOffers(
4342 broker, buyOfferIndex, minterOfferIndex),
4343 token::brokerFee(gwXAU(101)),
4349 env(token::brokerOffers(
4350 broker, buyOfferIndex, minterOfferIndex),
4351 token::brokerFee(gwXAU(100)));
4358 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4359 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4360 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4361 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4364 env(token::burn(buyer, nftID));
4371 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4372 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4377 uint256 const minterOfferIndex =
4379 env(token::createOffer(minter, nftID, gwXAU(900)),
4386 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4387 token::owner(minter));
4393 env(token::brokerOffers(
4394 broker, buyOfferIndex, minterOfferIndex),
4395 token::brokerFee(gwXAU(50)));
4402 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4403 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4404 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4405 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4408 env(token::burn(buyer, nftID));
4413 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4414 setXAUBalance({issuer, minter, buyer}, 1000, __LINE__);
4415 setXAUBalance({broker}, 500, __LINE__);
4419 uint256 const minterOfferIndex =
4421 env(token::createOffer(minter, nftID, gwXAU(900)),
4428 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4429 token::owner(minter));
4434 env(token::brokerOffers(
4435 broker, buyOfferIndex, minterOfferIndex),
4436 token::brokerFee(gwXAU(50)));
4442 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4443 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4444 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4445 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(550));
4448 env(token::burn(buyer, nftID));
4453 env(token::brokerOffers(
4454 broker, buyOfferIndex, minterOfferIndex),
4455 token::brokerFee(gwXAU(50)),
4462 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4463 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
4464 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000));
4465 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(500));
4468 env(token::burn(minter, nftID));
4479 testcase(
"NFToken offer owner");
4481 using namespace test::jtx;
4483 Env env{*
this, features};
4485 Account
const issuer{
"issuer"};
4486 Account
const buyer1{
"buyer1"};
4487 Account
const buyer2{
"buyer2"};
4488 env.fund(XRP(10000), issuer, buyer1, buyer2);
4497 BEAST_EXPECT(
nftCount(env, issuer) == 1);
4498 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4499 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4502 uint256 const buyer1OfferIndex =
4504 env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4505 uint256 const buyer2OfferIndex =
4507 env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4517 env.rpc(
"json",
"nft_buy_offers",
to_string(params));
4519 if (buyOffers.
isMember(jss::result) &&
4520 buyOffers[jss::result].
isMember(jss::offers))
4521 return buyOffers[jss::result][jss::offers].
size();
4527 BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4530 env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4534 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4535 BEAST_EXPECT(
nftCount(env, buyer1) == 1);
4536 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4540 BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4544 env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4548 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4549 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4550 BEAST_EXPECT(
nftCount(env, buyer2) == 1);
4553 BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4560 testcase(
"NFToken transactions with tickets");
4562 using namespace test::jtx;
4564 Env env{*
this, features};
4566 Account
const issuer{
"issuer"};
4567 Account
const buyer{
"buyer"};
4568 env.fund(XRP(10000), issuer, buyer);
4575 env(ticket::create(issuer, 10));
4581 env(ticket::create(buyer, 10));
4589 env(token::mint(issuer, 0u),
4591 ticket::use(issuerTicketSeq++));
4599 env(token::createOffer(buyer, nftId, XRP(1)),
4600 token::owner(issuer),
4601 ticket::use(buyerTicketSeq++));
4607 env(token::cancelOffer(buyer, {offerIndex0}),
4608 ticket::use(buyerTicketSeq++));
4615 env(token::createOffer(buyer, nftId, XRP(2)),
4616 token::owner(issuer),
4617 ticket::use(buyerTicketSeq++));
4623 env(token::acceptBuyOffer(issuer, offerIndex1),
4624 ticket::use(issuerTicketSeq++));
4631 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4638 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4639 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4650 testcase(
"NFToken delete account");
4652 using namespace test::jtx;
4654 Env env{*
this, features};
4656 Account
const issuer{
"issuer"};
4657 Account
const minter{
"minter"};
4658 Account
const becky{
"becky"};
4659 Account
const carla{
"carla"};
4660 Account
const daria{
"daria"};
4662 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4666 for (
int i = 0; i < 300; ++i)
4669 env(token::setMinter(issuer, minter));
4673 env(token::mint(minter, 0u),
4674 token::issuer(issuer),
4687 for (
int i = 0; i < 15; ++i)
4691 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4694 uint256 const carlaOfferIndex =
4696 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4701 env(acctdelete(becky, daria), fee(XRP(50)));
4705 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4710 env(acctdelete(minter, daria), fee(XRP(50)));
4722 for (
int i = 0; i < 15; ++i)
4727 env(token::burn(carla, nftId));
4730 env(acctdelete(issuer, daria), fee(XRP(50)));
4731 env(acctdelete(carla, daria), fee(XRP(50)));
4738 testcase(
"nft_buy_offers and nft_sell_offers");
4747 using namespace test::jtx;
4749 Env env{*
this, features};
4751 Account
const issuer{
"issuer"};
4752 Account
const buyer{
"buyer"};
4755 env.fund(XRP(1000000), issuer, buyer);
4764 auto checkOffers = [
this, &env, &nftID](
4765 char const* request,
4767 int expectMarkerCount,
4769 int markerCount = 0;
4776 Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4780 if (!marker.
empty())
4781 params[jss::marker] = marker;
4782 return env.rpc(
"json", request,
to_string(params));
4786 if (expectCount == 0)
4790 "expected \"result\"",
4795 nftOffers[jss::result].isMember(jss::error),
4796 "expected \"error\"",
4801 nftOffers[jss::result][jss::error].asString() ==
4803 "expected \"objectNotFound\"",
4814 "expected \"result\"",
4823 marker = result[jss::marker].
asString();
4828 "expected \"offers\"",
4834 allOffers.
append(someOffers[i]);
4837 }
while (!marker.
empty());
4841 allOffers.
size() == expectCount,
4842 "Unexpected returned offer count",
4846 markerCount == expectMarkerCount,
4847 "Unexpected marker count",
4857 globalFlags = offer[jss::flags].asInt();
4860 *globalFlags == offer[jss::flags].asInt(),
4861 "Inconsistent flags returned",
4867 offerIndexes.
insert(offer[jss::nft_offer_index].asString());
4868 amounts.
insert(offer[jss::amount].asString());
4872 offerIndexes.
size() == expectCount,
4873 "Duplicate indexes returned?",
4877 amounts.
size() == expectCount,
4878 "Duplicate amounts returned?",
4884 checkOffers(
"nft_sell_offers", 0,
false, __LINE__);
4888 auto makeSellOffers =
4889 [&env, &issuer, &nftID, &sellPrice](
STAmount const& limit) {
4892 while (sellPrice < limit)
4894 sellPrice += XRP(1);
4895 env(token::createOffer(issuer, nftID, sellPrice),
4897 if (++offerCount % 10 == 0)
4904 makeSellOffers(XRP(1));
4905 checkOffers(
"nft_sell_offers", 1, 0, __LINE__);
4908 makeSellOffers(XRP(250));
4909 checkOffers(
"nft_sell_offers", 250, 0, __LINE__);
4912 makeSellOffers(XRP(251));
4913 checkOffers(
"nft_sell_offers", 251, 1, __LINE__);
4916 makeSellOffers(XRP(500));
4917 checkOffers(
"nft_sell_offers", 500, 1, __LINE__);
4920 makeSellOffers(XRP(501));
4921 checkOffers(
"nft_sell_offers", 501, 2, __LINE__);
4924 checkOffers(
"nft_buy_offers", 0, 0, __LINE__);
4928 auto makeBuyOffers =
4929 [&env, &buyer, &issuer, &nftID, &buyPrice](
STAmount const& limit) {
4932 while (buyPrice < limit)
4935 env(token::createOffer(buyer, nftID, buyPrice),
4936 token::owner(issuer));
4937 if (++offerCount % 10 == 0)
4944 makeBuyOffers(XRP(1));
4945 checkOffers(
"nft_buy_offers", 1, 0, __LINE__);
4948 makeBuyOffers(XRP(250));
4949 checkOffers(
"nft_buy_offers", 250, 0, __LINE__);
4952 makeBuyOffers(XRP(251));
4953 checkOffers(
"nft_buy_offers", 251, 1, __LINE__);
4956 makeBuyOffers(XRP(500));
4957 checkOffers(
"nft_buy_offers", 500, 1, __LINE__);
4960 makeBuyOffers(XRP(501));
4961 checkOffers(
"nft_buy_offers", 501, 2, __LINE__);
4968 using namespace test::jtx;
4970 testcase(
"fixNFTokenNegOffer");
4972 Account
const issuer{
"issuer"};
4973 Account
const buyer{
"buyer"};
4974 Account
const gw{
"gw"};
4975 IOU
const gwXAU(gw[
"XAU"]);
4981 for (
auto const& tweakedFeatures :
4990 Env env{*
this, tweakedFeatures};
4992 env.fund(XRP(1000000), issuer, buyer, gw);
4995 env(trust(issuer, gwXAU(2000)));
4996 env(trust(buyer, gwXAU(2000)));
4999 env(pay(gw, issuer, gwXAU(1000)));
5000 env(pay(gw, buyer, gwXAU(1000)));
5020 uint256 const sellNegXrpOfferIndex =
5022 env(token::createOffer(issuer, nftID0, XRP(-2)),
5024 ter(offerCreateTER));
5027 uint256 const sellNegIouOfferIndex =
5029 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
5031 ter(offerCreateTER));
5034 uint256 const buyNegXrpOfferIndex =
5036 env(token::createOffer(buyer, nftID0, XRP(-1)),
5037 token::owner(issuer),
5038 ter(offerCreateTER));
5041 uint256 const buyNegIouOfferIndex =
5043 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
5044 token::owner(issuer),
5045 ter(offerCreateTER));
5057 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
5058 ter(offerAcceptTER));
5060 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
5061 ter(offerAcceptTER));
5065 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5066 ter(offerAcceptTER));
5068 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5069 ter(offerAcceptTER));
5082 env(token::brokerOffers(
5083 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5084 ter(offerAcceptTER));
5086 env(token::brokerOffers(
5087 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5088 ter(offerAcceptTER));
5100 env.fund(XRP(1000000), issuer, buyer, gw);
5103 env(trust(issuer, gwXAU(2000)));
5104 env(trust(buyer, gwXAU(2000)));
5107 env(pay(gw, issuer, gwXAU(1000)));
5108 env(pay(gw, buyer, gwXAU(1000)));
5124 uint256 const sellNegXrpOfferIndex =
5126 env(token::createOffer(issuer, nftID0, XRP(-2)),
5130 uint256 const sellNegIouOfferIndex =
5132 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
5136 uint256 const buyNegXrpOfferIndex =
5138 env(token::createOffer(buyer, nftID0, XRP(-1)),
5139 token::owner(issuer));
5142 uint256 const buyNegIouOfferIndex =
5144 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
5145 token::owner(issuer));
5154 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
5157 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
5162 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5165 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5170 env(token::brokerOffers(
5171 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5174 env(token::brokerOffers(
5175 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5182 for (
auto const& tweakedFeatures :
5186 Env env{*
this, tweakedFeatures};
5188 env.fund(XRP(1000000), issuer, buyer);
5200 env(token::createOffer(buyer, nftID, drops(1)),
5201 token::owner(issuer),
5202 token::destination(issuer),
5203 ter(offerCreateTER));
5211 using namespace test::jtx;
5213 testcase(
"Payments with IOU transfer fees");
5215 for (
auto const& tweakedFeatures :
5219 Env env{*
this, tweakedFeatures};
5221 Account
const minter{
"minter"};
5222 Account
const secondarySeller{
"seller"};
5223 Account
const buyer{
"buyer"};
5224 Account
const gw{
"gateway"};
5225 Account
const broker{
"broker"};
5226 IOU
const gwXAU(gw[
"XAU"]);
5227 IOU
const gwXPB(gw[
"XPB"]);
5229 env.fund(XRP(1000), gw, minter, secondarySeller, buyer, broker);
5232 env(trust(minter, gwXAU(2000)));
5233 env(trust(secondarySeller, gwXAU(2000)));
5234 env(trust(broker, gwXAU(10000)));
5235 env(trust(buyer, gwXAU(2000)));
5236 env(trust(buyer, gwXPB(2000)));
5240 env(rate(gw, 1.02));
5243 auto expectInitialState = [
this,
5256 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000));
5257 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(0));
5258 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(0));
5259 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(0));
5260 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(0));
5261 BEAST_EXPECT(env.balance(secondarySeller, gwXPB) == gwXPB(0));
5262 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5000));
5263 BEAST_EXPECT(env.balance(broker, gwXPB) == gwXPB(0));
5264 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-1000));
5265 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(0));
5266 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(0));
5267 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(0));
5269 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(0));
5271 env.balance(gw, secondarySeller[
"XPB"]) == gwXPB(0));
5272 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5000));
5273 BEAST_EXPECT(env.balance(gw, broker[
"XPB"]) == gwXPB(0));
5276 auto reinitializeTrustLineBalances = [&expectInitialState,
5285 if (
auto const difference =
5286 gwXAU(1000) - env.balance(buyer, gwXAU);
5287 difference > gwXAU(0))
5288 env(pay(gw, buyer, difference));
5289 if (env.balance(buyer, gwXPB) > gwXPB(0))
5290 env(pay(buyer, gw, env.balance(buyer, gwXPB)));
5291 if (env.balance(minter, gwXAU) > gwXAU(0))
5292 env(pay(minter, gw, env.balance(minter, gwXAU)));
5293 if (env.balance(minter, gwXPB) > gwXPB(0))
5294 env(pay(minter, gw, env.balance(minter, gwXPB)));
5295 if (env.balance(secondarySeller, gwXAU) > gwXAU(0))
5297 pay(secondarySeller,
5299 env.balance(secondarySeller, gwXAU)));
5300 if (env.balance(secondarySeller, gwXPB) > gwXPB(0))
5302 pay(secondarySeller,
5304 env.balance(secondarySeller, gwXPB)));
5305 auto brokerDiff = gwXAU(5000) - env.balance(broker, gwXAU);
5306 if (brokerDiff > gwXAU(0))
5307 env(pay(gw, broker, brokerDiff));
5308 else if (brokerDiff < gwXAU(0))
5310 brokerDiff.negate();
5311 env(pay(broker, gw, brokerDiff));
5313 if (env.balance(broker, gwXPB) > gwXPB(0))
5314 env(pay(broker, gw, env.balance(broker, gwXPB)));
5316 expectInitialState();
5319 auto mintNFT = [&env](Account
const& minter,
int transferFee = 0) {
5320 uint256 const nftID = token::getNextID(
5322 env(token::mint(minter),
5323 token::xferFee(transferFee),
5329 auto createBuyOffer =
5331 Account
const& offerer,
5332 Account
const& owner,
5338 env(token::createOffer(offerer, nftID, amount),
5339 token::owner(owner),
5340 terCode ? ter(*terCode)
5346 auto createSellOffer =
5348 Account
const& offerer,
5354 env(token::createOffer(offerer, nftID, amount),
5356 terCode ? ter(*terCode)
5365 reinitializeTrustLineBalances();
5366 auto const nftID = mintNFT(minter);
5367 auto const offerID =
5368 createSellOffer(minter, nftID, gwXAU(1000));
5372 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5376 expectInitialState();
5379 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5380 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5382 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5383 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5389 reinitializeTrustLineBalances();
5390 auto const nftID = mintNFT(minter);
5391 auto const offerID =
5392 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5396 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5400 expectInitialState();
5403 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5404 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5406 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5407 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5414 reinitializeTrustLineBalances();
5415 auto const nftID = mintNFT(minter);
5416 auto const offerID = createSellOffer(minter, nftID, gwXAU(995));
5420 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5424 expectInitialState();
5427 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(995));
5428 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-14.9));
5429 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-995));
5430 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(14.9));
5437 reinitializeTrustLineBalances();
5438 auto const nftID = mintNFT(minter);
5439 auto const offerID =
5440 createBuyOffer(buyer, minter, nftID, gwXAU(995));
5444 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5448 expectInitialState();
5451 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(995));
5452 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-14.9));
5453 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-995));
5454 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(14.9));
5462 reinitializeTrustLineBalances();
5463 auto const nftID = mintNFT(minter);
5464 auto const offerID = createSellOffer(minter, nftID, gwXAU(900));
5465 env(token::acceptSellOffer(buyer, offerID));
5468 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5469 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5470 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-900));
5471 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5478 reinitializeTrustLineBalances();
5479 auto const nftID = mintNFT(minter);
5480 auto const offerID =
5481 createBuyOffer(buyer, minter, nftID, gwXAU(900));
5482 env(token::acceptBuyOffer(minter, offerID));
5485 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5486 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5487 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-900));
5488 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5495 reinitializeTrustLineBalances();
5498 env(pay(gw, buyer, gwXAU(20)));
5501 auto const nftID = mintNFT(minter);
5502 auto const offerID =
5503 createSellOffer(minter, nftID, gwXAU(1000));
5504 env(token::acceptSellOffer(buyer, offerID));
5507 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5508 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5509 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5510 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5517 reinitializeTrustLineBalances();
5520 env(pay(gw, buyer, gwXAU(20)));
5523 auto const nftID = mintNFT(minter);
5524 auto const offerID =
5525 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5526 env(token::acceptBuyOffer(minter, offerID));
5529 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5530 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5531 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5532 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5537 reinitializeTrustLineBalances();
5539 auto const nftID = mintNFT(minter);
5540 auto const offerID =
5541 createSellOffer(minter, nftID, gwXAU(1000));
5545 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5550 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5552 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5555 expectInitialState();
5560 reinitializeTrustLineBalances();
5562 auto const nftID = mintNFT(minter);
5566 auto const offerID =
5567 createBuyOffer(gw, minter, nftID, gwXAU(1000), {offerTER});
5571 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5576 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5578 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5581 expectInitialState();
5586 reinitializeTrustLineBalances();
5587 auto const nftID = mintNFT(minter);
5588 auto const offerID =
5589 createSellOffer(minter, nftID, gwXAU(5000));
5593 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5598 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5600 env.balance(gw, minter[
"XAU"]) == gwXAU(-5000));
5603 expectInitialState();
5608 reinitializeTrustLineBalances();
5610 auto const nftID = mintNFT(minter);
5614 auto const offerID =
5615 createBuyOffer(gw, minter, nftID, gwXAU(5000), {offerTER});
5619 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5624 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5626 env.balance(gw, minter[
"XAU"]) == gwXAU(-5000));
5629 expectInitialState();
5635 reinitializeTrustLineBalances();
5636 auto const nftID = mintNFT(gw);
5637 auto const offerID = createSellOffer(gw, nftID, gwXAU(1000));
5638 env(token::acceptSellOffer(buyer, offerID));
5641 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5642 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5648 reinitializeTrustLineBalances();
5650 auto const nftID = mintNFT(gw);
5651 auto const offerID =
5652 createBuyOffer(buyer, gw, nftID, gwXAU(1000));
5653 env(token::acceptBuyOffer(gw, offerID));
5656 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5657 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5663 reinitializeTrustLineBalances();
5664 auto const nftID = mintNFT(gw);
5665 auto const offerID = createSellOffer(gw, nftID, gwXAU(2000));
5666 env(token::acceptSellOffer(buyer, offerID),
5669 expectInitialState();
5675 reinitializeTrustLineBalances();
5676 auto const nftID = mintNFT(gw);
5677 auto const offerID =
5678 createBuyOffer(buyer, gw, nftID, gwXAU(2000));
5679 env(token::acceptBuyOffer(gw, offerID),
5682 expectInitialState();
5687 reinitializeTrustLineBalances();
5688 auto const nftID = mintNFT(minter);
5689 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5690 env(token::acceptSellOffer(buyer, offerID),
5693 expectInitialState();
5698 reinitializeTrustLineBalances();
5699 auto const nftID = mintNFT(minter);
5700 auto const offerID = createBuyOffer(
5706 env(token::acceptBuyOffer(minter, offerID),
5709 expectInitialState();
5715 reinitializeTrustLineBalances();
5716 env(pay(gw, buyer, gwXPB(100)));
5719 auto const nftID = mintNFT(minter);
5720 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5721 env(token::acceptSellOffer(buyer, offerID));
5724 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5725 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5726 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(-10));
5727 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(-89.8));
5733 reinitializeTrustLineBalances();
5734 env(pay(gw, buyer, gwXPB(100)));
5737 auto const nftID = mintNFT(minter);
5738 auto const offerID =
5739 createBuyOffer(buyer, minter, nftID, gwXPB(10));
5740 env(token::acceptBuyOffer(minter, offerID));
5743 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5744 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5745 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(-10));
5746 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(-89.8));
5751 reinitializeTrustLineBalances();
5755 auto const nftID = mintNFT(minter, 3000);
5756 auto const primaryOfferID =
5757 createSellOffer(minter, nftID, XRP(0));
5758 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5762 auto const offerID =
5763 createSellOffer(secondarySeller, nftID, gwXAU(1000));
5767 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5771 expectInitialState();
5774 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(30));
5776 env.balance(secondarySeller, gwXAU) == gwXAU(970));
5777 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5778 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-30));
5780 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-970));
5781 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5787 reinitializeTrustLineBalances();
5791 auto const nftID = mintNFT(minter, 3000);
5792 auto const primaryOfferID =
5793 createSellOffer(minter, nftID, XRP(0));
5794 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5798 auto const offerID =
5799 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(1000));
5803 env(token::acceptBuyOffer(secondarySeller, offerID),
5808 expectInitialState();
5811 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(30));
5813 env.balance(secondarySeller, gwXAU) == gwXAU(970));
5814 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5815 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-30));
5817 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-970));
5818 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5824 reinitializeTrustLineBalances();
5828 auto const nftID = mintNFT(minter, 3000);
5829 auto const primaryOfferID =
5830 createSellOffer(minter, nftID, XRP(0));
5831 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5835 auto const offerID =
5836 createSellOffer(secondarySeller, nftID, gwXAU(900));
5837 env(token::acceptSellOffer(buyer, offerID));
5840 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5841 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5842 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5843 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-27));
5845 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-873));
5846 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5851 reinitializeTrustLineBalances();
5855 auto const nftID = mintNFT(minter, 3000);
5856 auto const primaryOfferID =
5857 createSellOffer(minter, nftID, XRP(0));
5858 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5862 auto const offerID =
5863 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(900));
5864 env(token::acceptBuyOffer(secondarySeller, offerID));
5868 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5870 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5872 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5873 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-27));
5875 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-873));
5876 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5894 reinitializeTrustLineBalances();
5896 auto const nftID = mintNFT(minter);
5897 auto const sellOffer =
5898 createSellOffer(minter, nftID, gwXAU(300));
5899 auto const buyOffer =
5900 createBuyOffer(buyer, minter, nftID, gwXAU(500));
5901 env(token::brokerOffers(broker, buyOffer, sellOffer),
5902 token::brokerFee(gwXAU(100)));
5905 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(400));
5906 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5907 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5908 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-400));
5909 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-490));
5910 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5100));
5928 reinitializeTrustLineBalances();
5932 auto const nftID = mintNFT(minter, 3000);
5933 auto const primaryOfferID =
5934 createSellOffer(minter, nftID, XRP(0));
5935 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5939 auto const sellOffer =
5940 createSellOffer(secondarySeller, nftID, gwXAU(300));
5941 auto const buyOffer =
5942 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(500));
5943 env(token::brokerOffers(broker, buyOffer, sellOffer),
5944 token::brokerFee(gwXAU(100)));
5947 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(12));
5948 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5949 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(388));
5950 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5951 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-12));
5952 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-490));
5954 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-388));
5955 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5100));
5975 testcase(
"Brokered sale to self");
5977 using namespace test::jtx;
5979 Account
const alice{
"alice"};
5980 Account
const bob{
"bob"};
5981 Account
const broker{
"broker"};
5983 Env env{*
this, features};
5984 env.fund(XRP(10000), alice, bob, broker);
6010 uint256 const bobBuyOfferIndex =
6012 env(token::createOffer(bob, nftId, XRP(5)), token::owner(alice));
6014 uint256 const aliceSellOfferIndex =
6016 env(token::createOffer(alice, nftId, XRP(0)),
6017 token::destination(bob),
6022 env(token::acceptSellOffer(bob, aliceSellOfferIndex));
6029 uint256 const bobSellOfferIndex =
6031 env(token::createOffer(bob, nftId, XRP(4)), txflags(
tfSellNFToken));
6036 BEAST_EXPECT(
nftCount(env, bob) == 1);
6037 auto const bobsPriorBalance = env.balance(bob);
6038 auto const brokersPriorBalance = env.balance(broker);
6042 env(token::brokerOffers(broker, bobBuyOfferIndex, bobSellOfferIndex),
6043 token::brokerFee(XRP(1)),
6052 BEAST_EXPECT(
nftCount(env, bob) == 1);
6053 BEAST_EXPECT(env.balance(bob) == bobsPriorBalance - XRP(1));
6055 env.balance(broker) ==
6056 brokersPriorBalance + XRP(1) - drops(10));
6062 BEAST_EXPECT(
nftCount(env, bob) == 1);
6063 BEAST_EXPECT(env.balance(bob) == bobsPriorBalance);
6065 env.balance(broker) == brokersPriorBalance - drops(10));
6072 using namespace test::jtx;
6074 testcase(
"fixNFTokenRemint");
6077 auto openLedgerSeq = [](Env& env) {
return env.current()->seq(); };
6082 auto incLgrSeqForAcctDel = [&](Env& env, Account
const& acct) {
6083 int const delta = [&]() ->
int {
6084 if (env.seq(acct) + 255 > openLedgerSeq(env))
6085 return env.seq(acct) - openLedgerSeq(env) + 255;
6088 BEAST_EXPECT(delta >= 0);
6089 for (
int i = 0; i < delta; ++i)
6091 BEAST_EXPECT(openLedgerSeq(env) == env.seq(acct) + 255);
6097 auto incLgrSeqForFixNftRemint = [&](Env& env, Account
const& acct) {
6099 auto const deletableLgrSeq =
6103 if (deletableLgrSeq > openLedgerSeq(env))
6104 delta = deletableLgrSeq - openLedgerSeq(env);
6106 BEAST_EXPECT(delta >= 0);
6107 for (
int i = 0; i < delta; ++i)
6109 BEAST_EXPECT(openLedgerSeq(env) == deletableLgrSeq);
6115 Env env{*
this, features};
6116 Account
const alice(
"alice");
6117 Account
const becky(
"becky");
6119 env.fund(XRP(10000), alice, becky);
6123 uint256 const prevNFTokenID = token::getNextID(env, alice, 0u);
6124 env(token::mint(alice));
6126 env(token::burn(alice, prevNFTokenID));
6133 incLgrSeqForAcctDel(env, alice);
6137 auto const acctDelFee{drops(env.current()->fees().increment)};
6138 env(acctdelete(alice, becky), fee(acctDelFee));
6143 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6144 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6147 env.fund(XRP(10000), alice);
6151 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6152 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6156 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
6157 env(token::mint(alice));
6161 env(token::burn(alice, remintNFTokenID));
6166 BEAST_EXPECT(remintNFTokenID != prevNFTokenID);
6169 BEAST_EXPECT(remintNFTokenID == prevNFTokenID);
6175 Env env{*
this, features};
6176 Account
const alice(
"alice");
6177 Account
const becky(
"becky");
6178 Account
const minter{
"minter"};
6180 env.fund(XRP(10000), alice, becky, minter);
6184 env(token::setMinter(alice, minter));
6190 for (
int i = 0; i < 500; i++)
6192 uint256 const nftokenID = token::getNextID(env, alice, 0u);
6194 env(token::mint(minter), token::issuer(alice));
6199 for (
auto const nftokenID : nftIDs)
6201 env(token::burn(minter, nftokenID));
6207 incLgrSeqForAcctDel(env, alice);
6211 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6212 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6214 auto const acctDelFee{drops(env.current()->fees().increment)};
6219 env(acctdelete(alice, becky), fee(acctDelFee));
6221 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6224 env.fund(XRP(10000), alice);
6228 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6229 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6234 uint256 const remintNFTokenID =
6235 token::getNextID(env, alice, 0u);
6236 env(token::mint(alice));
6240 env(token::burn(alice, remintNFTokenID));
6246 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) !=
6258 env(acctdelete(alice, becky),
6264 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6269 incLgrSeqForFixNftRemint(env, alice);
6272 env(acctdelete(alice, becky), fee(acctDelFee));
6277 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6278 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6281 env.fund(XRP(10000), alice);
6285 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6286 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6291 uint256 const remintNFTokenID =
6292 token::getNextID(env, alice, 0u);
6293 env(token::mint(alice));
6297 env(token::burn(alice, remintNFTokenID));
6303 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6311 Env env{*
this, features};
6313 Account
const alice{
"alice"};
6314 Account
const becky{
"becky"};
6315 env.fund(XRP(10000), alice, becky);
6322 env(ticket::create(alice, 100));
6331 for (
int i = 0; i < 50; i++)
6333 nftIDs.
push_back(token::getNextID(env, alice, 0u));
6334 env(token::mint(alice, 0u), ticket::use(aliceTicketSeq++));
6339 for (
auto const nftokenID : nftIDs)
6341 env(token::burn(alice, nftokenID),
6342 ticket::use(aliceTicketSeq++));
6350 incLgrSeqForAcctDel(env, alice);
6354 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6355 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6357 auto const acctDelFee{drops(env.current()->fees().increment)};
6362 env(acctdelete(alice, becky), fee(acctDelFee));
6367 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6368 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6371 env.fund(XRP(10000), alice);
6375 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6376 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6381 uint256 const remintNFTokenID =
6382 token::getNextID(env, alice, 0u);
6383 env(token::mint(alice));
6387 env(token::burn(alice, remintNFTokenID));
6393 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) !=
6405 env(acctdelete(alice, becky),
6411 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6416 incLgrSeqForFixNftRemint(env, alice);
6419 env(acctdelete(alice, becky), fee(acctDelFee));
6424 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6425 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6428 env.fund(XRP(10000), alice);
6432 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6433 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6438 uint256 const remintNFTokenID =
6439 token::getNextID(env, alice, 0u);
6440 env(token::mint(alice));
6444 env(token::burn(alice, remintNFTokenID));
6450 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6462 Env env{*
this, features};
6463 Account
const alice(
"alice");
6464 Account
const becky(
"becky");
6465 Account
const minter{
"minter"};
6467 env.fund(XRP(10000), alice, becky, minter);
6471 env(token::setMinter(alice, minter));
6476 env(ticket::create(minter, 100));
6480 BEAST_EXPECT(
ownerCount(env, minter) == 100);
6485 for (
int i = 0; i < 50; i++)
6487 uint256 const nftokenID = token::getNextID(env, alice, 0u);
6489 env(token::mint(minter),
6490 token::issuer(alice),
6491 ticket::use(minterTicketSeq++));
6496 for (
auto const nftokenID : nftIDs)
6498 env(token::burn(minter, nftokenID),
6499 ticket::use(minterTicketSeq++));
6507 incLgrSeqForAcctDel(env, alice);
6511 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6512 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6521 auto const acctDelFee{drops(env.current()->fees().increment)};
6522 env(acctdelete(alice, becky), fee(acctDelFee), ter(
tecTOO_SOON));
6526 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6531 incLgrSeqForFixNftRemint(env, alice);
6534 env(acctdelete(alice, becky), fee(acctDelFee));
6539 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6540 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6543 env.fund(XRP(10000), alice);
6547 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6548 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6553 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
6554 env(token::mint(alice));
6558 env(token::burn(alice, remintNFTokenID));
6564 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6583 testcase(
"Test synthetic fields from JSON response");
6585 using namespace test::jtx;
6587 Account
const alice{
"alice"};
6588 Account
const bob{
"bob"};
6589 Account
const broker{
"broker"};
6591 Env env{*
this, features};
6592 env.fund(XRP(10000), alice, bob, broker);
6598 auto verifyNFTokenID = [&](
uint256 const& actualNftID) {
6605 env.rpc(
"tx", txHash)[jss::result][jss::meta];
6608 if (!BEAST_EXPECT(meta.
isMember(jss::nftoken_id)))
6615 BEAST_EXPECT(nftID == actualNftID);
6620 auto verifyNFTokenIDsInCancelOffer =
6628 env.rpc(
"tx", txHash)[jss::result][jss::meta];
6631 if (!BEAST_EXPECT(meta.
isMember(jss::nftoken_ids)))
6637 meta[jss::nftoken_ids].begin(),
6638 meta[jss::nftoken_ids].end(),
6642 BEAST_EXPECT(nftID.
parseHex(
id.asString()));
6648 std::sort(actualNftIDs.begin(), actualNftIDs.end());
6651 BEAST_EXPECT(metaIDs.
size() == actualNftIDs.size());
6655 for (
size_t i = 0; i < metaIDs.
size(); ++i)
6656 BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
6661 auto verifyNFTokenOfferID = [&](
uint256 const& offerID) {
6668 env.rpc(
"tx", txHash)[jss::result][jss::meta];
6671 if (!BEAST_EXPECT(meta.
isMember(jss::offer_id)))
6676 BEAST_EXPECT(metaOfferID == offerID);
6687 verifyNFTokenID(nftId1);
6693 verifyNFTokenID(nftId2);
6698 uint256 const aliceOfferIndex1 =
6700 env(token::createOffer(alice, nftId1, drops(1)),
6703 verifyNFTokenOfferID(aliceOfferIndex1);
6705 uint256 const aliceOfferIndex2 =
6707 env(token::createOffer(alice, nftId2, drops(1)),
6710 verifyNFTokenOfferID(aliceOfferIndex2);
6715 env(token::cancelOffer(
6716 alice, {aliceOfferIndex1, aliceOfferIndex2}));
6718 verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
6722 auto const bobBuyOfferIndex =
6724 env(token::createOffer(bob, nftId1, drops(1)), token::owner(alice));
6726 verifyNFTokenOfferID(bobBuyOfferIndex);
6730 env(token::acceptBuyOffer(alice, bobBuyOfferIndex));
6732 verifyNFTokenID(nftId1);
6742 verifyNFTokenID(nftId);
6745 uint256 const offerAliceToBroker =
6747 env(token::createOffer(alice, nftId, drops(1)),
6748 token::destination(broker),
6751 verifyNFTokenOfferID(offerAliceToBroker);
6754 uint256 const offerBobToBroker =
6756 env(token::createOffer(bob, nftId, drops(1)), token::owner(alice));
6758 verifyNFTokenOfferID(offerBobToBroker);
6761 env(token::brokerOffers(
6762 broker, offerBobToBroker, offerAliceToBroker));
6764 verifyNFTokenID(nftId);
6775 verifyNFTokenID(nftId);
6778 uint256 const aliceOfferIndex1 =
6780 env(token::createOffer(alice, nftId, drops(1)),
6783 verifyNFTokenOfferID(aliceOfferIndex1);
6785 uint256 const aliceOfferIndex2 =
6787 env(token::createOffer(alice, nftId, drops(1)),
6790 verifyNFTokenOfferID(aliceOfferIndex2);
6794 env(token::cancelOffer(
6795 alice, {aliceOfferIndex1, aliceOfferIndex2}));
6797 verifyNFTokenIDsInCancelOffer({nftId});
6840 using namespace test::jtx;