20 #include <ripple/app/tx/impl/details/NFTokenUtils.h>
21 #include <ripple/basics/random.h>
22 #include <ripple/protocol/Feature.h>
23 #include <ripple/protocol/jss.h>
39 if (
auto const sleAcct = env.
le(acct))
49 if (
auto const sleIssuer = env.
le(issuer))
59 if (
auto const sleIssuer = env.
le(issuer))
69 params[jss::account] = acct.
human();
70 params[jss::type] =
"state";
72 return nfts[jss::result][jss::account_nfts].
size();
80 if (
auto const sleAcct = env.
le(acct))
89 return env.
current()->info().parentCloseTime.time_since_epoch().count();
97 using namespace test::jtx;
105 Account
const& master = env.master;
111 uint256 const nftId{token::getNextID(env, master, 0u)};
126 env(token::createOffer(master, nftId, XRP(10)), ter(
temDISABLED));
132 env(token::cancelOffer(master, {offerIndex}), ter(
temDISABLED));
138 env(token::acceptBuyOffer(master, offerIndex), ter(
temDISABLED));
147 Env env{*
this, features};
148 Account
const& master = env.master;
154 uint256 const nftId0{token::getNextID(env, env.master, 0u)};
155 env(token::mint(env.master, 0u));
161 env(token::burn(env.master, nftId0));
175 Account
const alice{
"alice"};
176 env.fund(XRP(10000), alice);
178 uint256 const aliceOfferIndex =
180 env(token::createOffer(alice, nftId1, XRP(1000)),
181 token::owner(master));
192 env(token::acceptBuyOffer(master, aliceOfferIndex));
209 testcase(
"Mint reserve");
211 using namespace test::jtx;
213 Env env{*
this, features};
214 Account
const alice{
"alice"};
215 Account
const minter{
"minter"};
220 env.fund(XRP(200), alice, minter);
222 BEAST_EXPECT(env.balance(alice) == XRP(200));
223 BEAST_EXPECT(env.balance(minter) == XRP(200));
235 env(pay(env.master, alice, XRP(50) + drops(9)));
240 auto checkAliceOwnerMintedBurned = [&env,
this, &alice](
253 ss <<
"Wrong " << type <<
" count. Found: " << found
254 <<
"; Expected: " << exp;
255 fail(ss.
str(), __FILE__, line);
258 oneCheck(
"owner",
ownerCount(env, alice), owners);
259 oneCheck(
"minted",
mintedCount(env, alice), minted);
260 oneCheck(
"burned",
burnedCount(env, alice), burned);
266 checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
269 env(pay(env.master, alice, drops(11)));
273 env(token::mint(alice));
275 checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
279 for (
int i = 1; i < 32; ++i)
281 env(token::mint(alice));
282 checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
289 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
292 env(pay(env.master, alice, XRP(50) + drops(329)));
298 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
301 env(pay(env.master, alice, drops(11)));
305 env(token::mint(alice));
307 checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
314 env(token::burn(alice, token::getID(alice, 0, seq++)));
316 checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
320 env(token::burn(alice, token::getID(alice, 197, 5)), ter(
tecNO_ENTRY));
322 checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
326 env(token::setMinter(alice, minter));
333 auto checkMintersOwnerMintedBurned = [&env,
this, &alice, &minter](
341 auto oneCheck = [
this](
351 ss <<
"Wrong " << type <<
" count. Found: " << found
352 <<
"; Expected: " << exp;
353 fail(ss.
str(), __FILE__, line);
356 oneCheck(
"alice owner",
ownerCount(env, alice), aliceOwners, line);
358 "alice minted",
mintedCount(env, alice), aliceMinted, line);
360 "alice burned",
burnedCount(env, alice), aliceBurned, line);
362 "minter owner",
ownerCount(env, minter), minterOwners, line);
364 "minter minted",
mintedCount(env, minter), minterMinted, line);
366 "minter burned",
burnedCount(env, minter), minterBurned, line);
372 env(pay(env.master, minter, XRP(50) - drops(1)));
374 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
379 env(token::mint(minter),
380 token::issuer(alice),
384 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
387 env(pay(env.master, minter, drops(11)));
391 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
393 checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
397 for (
int i = 1; i < 32; ++i)
399 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
400 checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
404 env(pay(env.master, minter, XRP(50) + drops(319)));
409 env(token::mint(minter),
410 token::issuer(alice),
414 checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
417 env(pay(env.master, minter, drops(11)));
421 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
423 checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
428 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
430 checkMintersOwnerMintedBurned(
431 0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
435 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
437 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
440 env(token::burn(minter, token::getID(alice, 2009, 3)),
443 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
451 testcase(
"Mint max tokens");
453 using namespace test::jtx;
455 Account
const alice{
"alice"};
456 Env env{*
this, features};
457 env.fund(XRP(1000), alice);
466 uint256 const nftId0{token::getNextID(env, alice, 0u)};
467 env(token::mint(alice, 0u));
470 env(token::burn(alice, nftId0));
477 env.app().openLedger().modify(
486 auto replacement = std::make_shared<SLE>(*sle, sle->key());
492 view.rawReplace(replacement);
496 // See whether alice is at the boundary that causes an error.
497 env(token::mint(alice, 0u), ter(tesSUCCESS));
498 env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
502 testMintInvalid(FeatureBitset features)
504 // Explore many of the invalid ways to mint an NFT.
505 testcase("Mint invalid");
507 using namespace test::jtx;
509 Env env{*this, features};
510 Account const alice{"alice"};
511 Account const minter{"minter"};
513 // Fund alice and minter enough to exist, but not enough to meet
514 // the reserve for creating their first NFT. Account reserve for unit
515 // tests is 200 XRP, not 20.
516 env.fund(XRP(200), alice, minter);
519 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
522 // Fund alice enough to start minting NFTs.
523 env(pay(env.master, alice, XRP(1000)));
526 //----------------------------------------------------------------------
529 // Set a negative fee.
530 env(token::mint(alice, 0u),
531 fee(STAmount(10ull, true)),
534 // Set an invalid flag.
535 env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
537 // Can't
set a transfer fee
if the NFT does not have the tfTRANSFERABLE
539 env(token::mint(alice, 0u),
544 env(token::mint(alice, 0u),
550 env(token::mint(alice, 0u), token::issuer(alice), ter(
temMALFORMED));
553 env(token::mint(alice, 0u), token::uri(
""), ter(
temMALFORMED));
556 env(token::mint(alice, 0u),
564 env(token::mint(alice, 0u),
565 token::issuer(Account(
"demon")),
572 env(token::mint(minter, 0u),
573 token::issuer(alice),
581 testcase(
"Burn invalid");
583 using namespace test::jtx;
585 Env env{*
this, features};
586 Account
const alice{
"alice"};
587 Account
const buyer{
"buyer"};
588 Account
const minter{
"minter"};
589 Account
const gw(
"gw");
590 IOU
const gwAUD(gw[
"AUD"]);
595 env.fund(XRP(250), alice, buyer, minter, gw);
609 env(token::burn(alice, nftAlice0ID),
616 env(token::burn(alice, nftAlice0ID),
626 env(token::burn(alice, token::getID(alice, 0, 1)), ter(
tecNO_ENTRY));
640 testcase(
"Invalid NFT offer create");
642 using namespace test::jtx;
644 Env env{*
this, features};
645 Account
const alice{
"alice"};
646 Account
const buyer{
"buyer"};
647 Account
const gw(
"gw");
648 IOU
const gwAUD(gw[
"AUD"]);
653 env.fund(XRP(250), alice, buyer, gw);
659 env(token::mint(alice, 0u),
671 uint256 nftNoXferID = token::getNextID(env, alice, 0);
672 env(token::mint(alice, 0));
685 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
692 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
699 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
706 env(token::createOffer(buyer, nftXrpOnlyID, buyer[
"USD"](1)),
708 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](0)),
710 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
716 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](1)),
717 token::expiration(0),
724 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
730 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
738 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
745 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
746 token::destination(alice),
753 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
754 token::destination(Account(
"demon")),
764 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
772 env(token::createOffer(buyer, token::getID(alice, 0, 1), XRP(1000)),
779 env(token::createOffer(alice, token::getID(alice, 0, 1), XRP(1000)),
786 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
792 env(trust(buyer, gwAUD(1000)));
798 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
810 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
821 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
831 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
840 env(trust(buyer, gwAUD(1000)));
843 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
853 env(pay(gw, buyer, gwAUD(999)));
858 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
865 env(pay(env.master, buyer, XRP(50) + drops(119)));
868 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
875 env(pay(env.master, buyer, drops(11)));
880 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
890 testcase(
"Invalid NFT offer cancel");
892 using namespace test::jtx;
894 Env env{*
this, features};
895 Account
const alice{
"alice"};
896 Account
const buyer{
"buyer"};
897 Account
const gw(
"gw");
898 IOU
const gwAUD(gw[
"AUD"]);
900 env.fund(XRP(1000), alice, buyer, gw);
911 uint256 const buyerOfferIndex =
913 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
923 env(token::cancelOffer(buyer, {buyerOfferIndex}),
930 env(token::cancelOffer(buyer, {buyerOfferIndex}),
950 env(token::cancelOffer(buyer, offers), ter(
temMALFORMED));
956 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
970 env(pay(env.master, gw, XRP(5000)));
974 env(offer(gw, XRP(i), gwAUD(1)));
981 env(check::create(gw, env.master, XRP(300)));
988 env(check::cancel(gw, gwCheckId));
1002 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1010 testcase(
"Invalid NFT offer accept");
1012 using namespace test::jtx;
1014 Env env{*
this, features};
1015 Account
const alice{
"alice"};
1016 Account
const buyer{
"buyer"};
1017 Account
const gw(
"gw");
1018 IOU
const gwAUD(gw[
"AUD"]);
1020 env.fund(XRP(1000), alice, buyer, gw);
1036 uint256 nftNoXferID = token::getNextID(env, alice, 0);
1037 env(token::mint(alice, 0));
1042 uint256 const plainOfferIndex =
1044 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1051 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1056 uint256 const xrpOnlyOfferIndex =
1058 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1063 uint256 const noXferOfferIndex =
1065 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1071 uint256 const aliceExpOfferIndex =
1073 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1083 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1090 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1091 txflags(0x00008000),
1098 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1107 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1117 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1126 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1127 token::brokerFee(gwAUD(0)),
1136 env(token::acceptBuyOffer(buyer, beast::zero),
1143 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1149 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1154 env(token::acceptSellOffer(buyer, beast::zero),
1160 env(token::acceptSellOffer(buyer, missingOfferIndex),
1166 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1175 env(trust(alice, gwAUD(1000)));
1176 env(trust(buyer, gwAUD(1000)));
1178 env(pay(gw, buyer, gwAUD(30)));
1187 uint256 const buyerOfferIndex =
1189 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1190 token::owner(alice));
1195 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1201 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1208 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1214 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1220 uint256 const buyerOfferIndex =
1222 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1223 token::owner(alice));
1229 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1230 token::brokerFee(XRP(40)),
1236 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1237 token::brokerFee(gwAUD(31)),
1244 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1245 token::brokerFee(gwAUD(1.5)),
1251 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1259 uint256 const buyerOfferIndex =
1261 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1262 token::owner(alice));
1267 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1273 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1279 env(pay(buyer, gw, gwAUD(30)));
1281 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1282 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1291 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1294 env(token::acceptSellOffer(gw, offerIndex));
1298 env(pay(gw, buyer, gwAUD(30)));
1302 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1308 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1316 uint256 const buyerOfferIndex =
1318 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1319 token::owner(alice));
1324 env(token::acceptSellOffer(alice, buyerOfferIndex),
1330 env(token::acceptSellOffer(alice, plainOfferIndex),
1337 env(token::acceptSellOffer(buyer, plainOfferIndex),
1348 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1351 env(token::acceptSellOffer(alice, offerIndex));
1355 env(pay(buyer, gw, gwAUD(30)));
1357 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1358 env(token::acceptSellOffer(buyer, audOfferIndex),
1375 testcase(
"Mint flagBurnable");
1377 using namespace test::jtx;
1379 Env env{*
this, features};
1380 Account
const alice{
"alice"};
1381 Account
const buyer{
"buyer"};
1382 Account
const minter1{
"minter1"};
1383 Account
const minter2{
"minter2"};
1385 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1390 env(token::setMinter(alice, minter1));
1397 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1399 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1400 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1405 env(token::createOffer(minter1, nftID, XRP(0)),
1409 env(token::acceptSellOffer(buyer, offerIndex));
1417 uint256 const noBurnID = nftToBuyer(0);
1418 env(token::burn(alice, noBurnID),
1419 token::owner(buyer),
1422 env(token::burn(minter1, noBurnID),
1423 token::owner(buyer),
1426 env(token::burn(minter2, noBurnID),
1427 token::owner(buyer),
1432 env(token::burn(buyer, noBurnID), token::owner(buyer));
1439 env(token::burn(minter2, burnableID),
1440 token::owner(buyer),
1445 env(token::burn(alice, burnableID), token::owner(buyer));
1453 env(token::burn(buyer, burnableID));
1461 env(token::burn(buyer, burnableID), token::owner(buyer));
1471 env(token::setMinter(alice, minter2));
1476 env(token::burn(minter1, burnableID),
1477 token::owner(buyer),
1483 env(token::burn(minter2, burnableID), token::owner(buyer));
1493 testcase(
"Mint flagOnlyXRP");
1495 using namespace test::jtx;
1497 Env env{*
this, features};
1498 Account
const alice{
"alice"};
1499 Account
const buyer{
"buyer"};
1500 Account
const gw(
"gw");
1501 IOU
const gwAUD(gw[
"AUD"]);
1504 env.fund(XRP(1000), alice, buyer, gw);
1506 env(trust(alice, gwAUD(1000)));
1507 env(trust(buyer, gwAUD(1000)));
1509 env(pay(gw, buyer, gwAUD(100)));
1519 uint256 const aliceOfferIndex =
1521 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1527 uint256 const buyerOfferIndex =
1529 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1530 token::owner(alice));
1535 env(token::cancelOffer(alice, {aliceOfferIndex}));
1536 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1542 env(token::burn(alice, nftIOUsOkayID));
1555 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1562 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1563 token::owner(alice),
1570 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1576 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1577 token::owner(alice));
1587 testcase(
"Mint flagCreateTrustLines");
1589 using namespace test::jtx;
1591 Account
const alice{
"alice"};
1592 Account
const becky{
"becky"};
1593 Account
const cheri{
"cheri"};
1594 Account
const gw(
"gw");
1595 IOU
const gwAUD(gw[
"AUD"]);
1596 IOU
const gwCAD(gw[
"CAD"]);
1597 IOU
const gwEUR(gw[
"EUR"]);
1602 for (
auto const& tweakedFeatures :
1606 Env env{*
this, tweakedFeatures};
1607 env.fund(XRP(1000), alice, becky, cheri, gw);
1611 env(trust(becky, gwAUD(1000)));
1612 env(trust(cheri, gwAUD(1000)));
1613 env(trust(becky, gwCAD(1000)));
1614 env(trust(cheri, gwCAD(1000)));
1615 env(trust(becky, gwEUR(1000)));
1616 env(trust(cheri, gwEUR(1000)));
1618 env(pay(gw, becky, gwAUD(500)));
1619 env(pay(gw, becky, gwCAD(500)));
1620 env(pay(gw, becky, gwEUR(500)));
1621 env(pay(gw, cheri, gwAUD(500)));
1622 env(pay(gw, cheri, gwCAD(500)));
1629 uint256 const nftNoAutoTrustID{
1631 env(token::mint(alice, 0u),
1632 token::xferFee(xferFee),
1637 uint256 const beckyBuyOfferIndex =
1639 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1640 token::owner(alice));
1642 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1646 TER const createOfferTER =
1648 uint256 const beckyOfferIndex =
1650 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1652 ter(createOfferTER));
1656 uint256 const cheriOfferIndex =
1658 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1659 token::owner(becky),
1660 ter(createOfferTER));
1664 env(token::cancelOffer(becky, {beckyOfferIndex}));
1665 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1673 uint256 const nftAutoTrustID{token::getNextID(
1684 env(token::mint(alice, 0u),
1685 token::xferFee(transferFee),
1696 uint256 const beckyBuyOfferIndex =
1698 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1699 token::owner(alice));
1701 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1705 uint256 const beckySellOfferIndex =
1707 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1710 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1714 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1717 uint256 const beckyBuyBackOfferIndex =
1719 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1720 token::owner(cheri));
1722 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1726 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1727 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1733 uint256 const nftNoAutoTrustID{token::getNextID(
1735 env(token::mint(alice, 0u),
1736 token::xferFee(transferFee),
1741 uint256 const aliceSellOfferIndex =
1743 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1746 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1752 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1755 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1759 uint256 const cheriSellOfferIndex =
1761 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1764 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1770 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1779 testcase(
"Mint flagTransferable");
1781 using namespace test::jtx;
1783 Env env{*
this, features};
1785 Account
const alice{
"alice"};
1786 Account
const becky{
"becky"};
1787 Account
const minter{
"minter"};
1789 env.fund(XRP(1000), alice, becky, minter);
1795 uint256 const nftAliceNoTransferID{
1796 token::getNextID(env, alice, 0u)};
1797 env(token::mint(alice, 0u), token::xferFee(0));
1803 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1804 token::owner(alice),
1808 uint256 const aliceSellOfferIndex =
1810 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1813 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1819 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1828 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1830 token::destination(alice),
1838 uint256 const aliceBuyOfferIndex =
1840 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1841 token::owner(becky));
1843 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1849 env(token::burn(alice, nftAliceNoTransferID));
1856 env(token::setMinter(alice, minter));
1860 uint256 const nftMinterNoTransferID{
1861 token::getNextID(env, alice, 0u)};
1862 env(token::mint(minter), token::issuer(alice));
1868 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1869 token::owner(minter),
1875 env(token::clearMinter(alice));
1880 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1888 for (
int i = 0; i < 10; ++i)
1891 env(token::setMinter(alice, minter));
1897 uint256 const minterSellOfferIndex =
1899 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1906 env(token::clearMinter(alice));
1912 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1918 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1926 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1927 token::owner(becky),
1934 uint256 const aliceBuyOfferIndex =
1936 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1937 token::owner(becky));
1943 for (
int i = 0; i < 10; ++i)
1946 env(token::setMinter(alice, minter));
1951 uint256 const minterBuyOfferIndex =
1953 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1954 token::owner(becky));
1960 env(token::clearMinter(alice));
1966 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1974 env(token::burn(minter, nftMinterNoTransferID), ter(
tesSUCCESS));
1976 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1993 uint256 const aliceSellOfferIndex =
1995 env(token::createOffer(alice, nftAliceID, XRP(20)),
2000 uint256 const beckyBuyOfferIndex =
2002 env(token::createOffer(becky, nftAliceID, XRP(21)),
2003 token::owner(alice));
2008 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
2014 uint256 const beckySellOfferIndex =
2016 env(token::createOffer(becky, nftAliceID, XRP(22)),
2024 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2031 uint256 const minterSellOfferIndex =
2033 env(token::createOffer(minter, nftAliceID, XRP(23)),
2041 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2049 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2057 env(token::burn(becky, nftAliceID));
2069 testcase(
"Mint transferFee");
2071 using namespace test::jtx;
2073 Env env{*
this, features};
2075 Account
const alice{
"alice"};
2076 Account
const becky{
"becky"};
2077 Account
const carol{
"carol"};
2078 Account
const minter{
"minter"};
2079 Account
const gw{
"gw"};
2080 IOU
const gwXAU(gw[
"XAU"]);
2082 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2085 env(trust(alice, gwXAU(2000)));
2086 env(trust(becky, gwXAU(2000)));
2087 env(trust(carol, gwXAU(2000)));
2088 env(trust(minter, gwXAU(2000)));
2090 env(pay(gw, alice, gwXAU(1000)));
2091 env(pay(gw, becky, gwXAU(1000)));
2092 env(pay(gw, carol, gwXAU(1000)));
2093 env(pay(gw, minter, gwXAU(1000)));
2098 env(token::setMinter(alice, minter));
2115 uint256 const beckyBuyOfferIndex =
2117 env(token::createOffer(becky, nftID, gwXAU(10)),
2118 token::owner(alice));
2120 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2121 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2123 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2125 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2126 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2129 uint256 const beckySellOfferIndex =
2131 env(token::createOffer(becky, nftID, gwXAU(10)),
2134 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2136 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2137 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2138 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2141 uint256 const minterBuyOfferIndex =
2143 env(token::createOffer(minter, nftID, gwXAU(10)),
2144 token::owner(carol));
2146 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2148 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2149 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2150 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2151 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2155 uint256 const minterSellOfferIndex =
2157 env(token::createOffer(minter, nftID, gwXAU(10)),
2160 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2162 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2163 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2164 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2165 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2168 env(token::burn(alice, nftID));
2181 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2185 uint256 const beckyBuyOfferIndex =
2187 env(token::createOffer(becky, nftID, gwXAU(10)),
2188 token::owner(alice));
2190 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2191 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2193 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2195 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2196 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2199 uint256 const beckySellOfferIndex =
2201 env(token::createOffer(becky, nftID, gwXAU(10)),
2204 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2207 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2208 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2209 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2212 uint256 const minterBuyOfferIndex =
2214 env(token::createOffer(minter, nftID, gwXAU(10)),
2215 token::owner(carol));
2217 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2220 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2221 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2222 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2223 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2227 uint256 const minterSellOfferIndex =
2229 env(token::createOffer(minter, nftID, gwXAU(10)),
2232 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2234 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2235 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2236 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2237 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2241 env(pay(alice, becky, gwXAU(0.0001)));
2242 env(pay(alice, carol, gwXAU(0.0001)));
2245 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2246 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2247 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2248 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2251 env(token::burn(alice, nftID));
2262 env(token::mint(alice),
2269 uint256 const nftID = token::getNextID(
2271 env(token::mint(alice),
2277 uint256 const beckyBuyOfferIndex =
2279 env(token::createOffer(becky, nftID, gwXAU(10)),
2280 token::owner(alice));
2282 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2283 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2285 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2287 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2288 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2291 uint256 const beckySellOfferIndex =
2293 env(token::createOffer(becky, nftID, gwXAU(100)),
2296 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2299 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2300 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2301 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2304 uint256 const carolBuyOfferIndex =
2306 env(token::createOffer(carol, nftID, gwXAU(10)),
2307 token::owner(minter));
2309 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2312 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2313 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2314 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2315 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2319 uint256 const carolSellOfferIndex =
2321 env(token::createOffer(carol, nftID, gwXAU(10)),
2324 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2327 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2328 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2329 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2330 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2333 env(pay(alice, minter, gwXAU(55)));
2334 env(pay(becky, minter, gwXAU(40)));
2336 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2337 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2338 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2339 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2342 env(token::burn(alice, nftID));
2352 for (
auto NumberSwitchOver : {
true})
2354 if (NumberSwitchOver)
2362 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2368 STAmount aliceBalance = env.balance(alice);
2369 STAmount minterBalance = env.balance(minter);
2370 uint256 const minterBuyOfferIndex =
2372 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2374 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2376 aliceBalance += XRP(1) - fee;
2377 minterBalance -= XRP(1) + fee;
2378 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2379 BEAST_EXPECT(env.balance(minter) == minterBalance);
2383 auto pmt = NumberSwitchOver ? drops(50000) : drops(99999);
2384 STAmount carolBalance = env.balance(carol);
2385 uint256 const minterSellOfferIndex =
2387 env(token::createOffer(minter, nftID, pmt), txflags(
tfSellNFToken));
2389 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2391 minterBalance += pmt - fee;
2392 carolBalance -= pmt + fee;
2393 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2394 BEAST_EXPECT(env.balance(minter) == minterBalance);
2395 BEAST_EXPECT(env.balance(carol) == carolBalance);
2399 STAmount beckyBalance = env.balance(becky);
2400 uint256 const beckyBuyOfferIndex =
2402 pmt = NumberSwitchOver ? drops(50001) : drops(100000);
2403 env(token::createOffer(becky, nftID, pmt), token::owner(carol));
2405 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2407 carolBalance += pmt - drops(1) - fee;
2408 beckyBalance -= pmt + fee;
2409 aliceBalance += drops(1);
2411 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2412 BEAST_EXPECT(env.balance(minter) == minterBalance);
2413 BEAST_EXPECT(env.balance(carol) == carolBalance);
2414 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2423 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2429 env(pay(alice, gw, env.balance(alice, gwXAU)));
2430 env(pay(minter, gw, env.balance(minter, gwXAU)));
2431 env(pay(becky, gw, env.balance(becky, gwXAU)));
2436 env(pay(gw, alice, startXAUBalance));
2437 env(pay(gw, minter, startXAUBalance));
2438 env(pay(gw, becky, startXAUBalance));
2447 STAmount aliceBalance = env.balance(alice, gwXAU);
2448 STAmount minterBalance = env.balance(minter, gwXAU);
2449 uint256 const minterBuyOfferIndex =
2451 env(token::createOffer(minter, nftID, tinyXAU),
2452 token::owner(alice));
2454 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2456 aliceBalance += tinyXAU;
2457 minterBalance -= tinyXAU;
2458 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2459 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2462 STAmount carolBalance = env.balance(carol, gwXAU);
2463 uint256 const minterSellOfferIndex =
2465 env(token::createOffer(minter, nftID, tinyXAU),
2468 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2471 minterBalance += tinyXAU;
2472 carolBalance -= tinyXAU;
2474 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2475 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2476 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2483 STAmount beckyBalance = env.balance(becky, gwXAU);
2484 uint256 const beckyBuyOfferIndex =
2486 env(token::createOffer(becky, nftID, cheapNFT),
2487 token::owner(carol));
2489 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2492 aliceBalance += tinyXAU;
2493 beckyBalance -= cheapNFT;
2494 carolBalance += cheapNFT - tinyXAU;
2495 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2496 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2497 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2498 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2506 testcase(
"Mint taxon");
2508 using namespace test::jtx;
2510 Env env{*
this, features};
2512 Account
const alice{
"alice"};
2513 Account
const becky{
"becky"};
2515 env.fund(XRP(1000), alice, becky);
2524 uint256 const nftID = token::getNextID(env, alice, 0u);
2531 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2539 for (
int i = 0; i < 10; ++i)
2549 ss <<
"Taxon recovery failed from nftID "
2550 <<
to_string(nftID) <<
". Expected: " << taxon
2551 <<
"; got: " << gotTaxon;
2556 uint256 const nftAliceID = token::getID(
2559 rand_int<std::uint32_t>(),
2560 rand_int<std::uint16_t>(),
2561 rand_int<std::uint16_t>());
2562 check(taxon, nftAliceID);
2564 uint256 const nftBeckyID = token::getID(
2567 rand_int<std::uint32_t>(),
2568 rand_int<std::uint16_t>(),
2569 rand_int<std::uint16_t>());
2570 check(taxon, nftBeckyID);
2582 testcase(
"Mint URI");
2584 using namespace test::jtx;
2586 Env env{*
this, features};
2588 Account
const alice{
"alice"};
2589 Account
const becky{
"becky"};
2591 env.fund(XRP(10000), alice, becky);
2597 auto randURI = []() {
2619 : uri(std::move(uri_)), taxon(taxon_)
2627 entries.
emplace_back(randURI(), rand_int<std::uint32_t>());
2630 for (Entry
const& entry : entries)
2632 if (entry.uri.empty())
2634 env(token::mint(alice, entry.taxon));
2638 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2646 params[jss::account] = alice.human();
2647 params[jss::type] =
"state";
2648 return env.rpc(
"json",
"account_nfts",
to_string(params));
2652 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2653 if (!BEAST_EXPECT(nfts.
size() == entries.size()))
2666 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2671 Entry
const& entry = entries[i];
2674 if (entry.uri.empty())
2689 testcase(
"Create offer destination");
2691 using namespace test::jtx;
2693 Env env{*
this, features};
2695 Account
const issuer{
"issuer"};
2696 Account
const minter{
"minter"};
2697 Account
const buyer{
"buyer"};
2698 Account
const broker{
"broker"};
2700 env.fund(XRP(1000), issuer, minter, buyer, broker);
2704 env(token::setMinter(issuer, minter));
2709 env(token::mint(minter, 0),
2710 token::issuer(issuer),
2717 uint256 const offerMinterToIssuer =
2719 env(token::createOffer(minter, nftokenID, drops(1)),
2720 token::destination(issuer),
2723 uint256 const offerMinterToBuyer =
2725 env(token::createOffer(minter, nftokenID, drops(1)),
2726 token::destination(buyer),
2729 uint256 const offerIssuerToMinter =
2731 env(token::createOffer(issuer, nftokenID, drops(1)),
2732 token::owner(minter),
2733 token::destination(minter));
2735 uint256 const offerIssuerToBuyer =
2737 env(token::createOffer(issuer, nftokenID, drops(1)),
2738 token::owner(minter),
2739 token::destination(buyer));
2753 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2755 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2757 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2759 env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2768 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2769 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2770 env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2771 env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2781 uint256 const offerMinterSellsToBuyer =
2783 env(token::createOffer(minter, nftokenID, drops(1)),
2784 token::destination(buyer),
2793 env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2801 env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2811 uint256 const offerMinterBuysFromBuyer =
2813 env(token::createOffer(minter, nftokenID, drops(1)),
2814 token::owner(buyer),
2815 token::destination(buyer));
2823 env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2831 env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2840 uint256 const offerBuyerBuysFromMinter =
2842 env(token::createOffer(buyer, nftokenID, drops(1)),
2843 token::owner(minter),
2844 token::destination(broker));
2850 env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2855 env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2865 uint256 const offerMinterToBroker =
2867 env(token::createOffer(minter, nftokenID, drops(1)),
2868 token::destination(broker),
2871 uint256 const offerBuyerToMinter =
2873 env(token::createOffer(buyer, nftokenID, drops(1)),
2874 token::owner(minter));
2887 env(token::brokerOffers(
2888 issuer, offerBuyerToMinter, offerMinterToBroker),
2898 env(token::brokerOffers(
2899 broker, offerBuyerToMinter, offerMinterToBroker));
2910 uint256 const offerBuyerToMinter =
2912 env(token::createOffer(buyer, nftokenID, drops(1)),
2913 token::destination(minter),
2916 uint256 const offerMinterToBuyer =
2918 env(token::createOffer(minter, nftokenID, drops(1)),
2919 token::owner(buyer));
2921 uint256 const offerIssuerToBuyer =
2923 env(token::createOffer(issuer, nftokenID, drops(1)),
2924 token::owner(buyer));
2937 env(token::brokerOffers(
2938 broker, offerIssuerToBuyer, offerBuyerToMinter),
2951 env(token::brokerOffers(
2952 broker, offerMinterToBuyer, offerBuyerToMinter),
2958 env(token::acceptBuyOffer(buyer, offerMinterToBuyer));
2962 env(token::cancelOffer(buyer, {offerBuyerToMinter}));
2970 env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
2982 uint256 const offerMinterToBroker =
2984 env(token::createOffer(minter, nftokenID, drops(1)),
2985 token::destination(broker),
2988 uint256 const offerBuyerToBroker =
2990 env(token::createOffer(buyer, nftokenID, drops(1)),
2991 token::owner(minter),
2992 token::destination(broker));
3000 env(token::brokerOffers(
3001 issuer, offerBuyerToBroker, offerMinterToBroker),
3010 env(token::brokerOffers(
3011 broker, offerBuyerToBroker, offerMinterToBroker));
3022 testcase(
"Create offer destination disallow incoming");
3024 using namespace test::jtx;
3029 Account
const alice{
"alice"};
3030 env.fund(XRP(10000), alice);
3033 auto const sle = env.le(alice);
3034 uint32_t flags = sle->getFlags();
3040 Account
const issuer{
"issuer"};
3041 Account
const minter{
"minter"};
3042 Account
const buyer{
"buyer"};
3043 Account
const alice{
"alice"};
3045 env.fund(XRP(1000), issuer, minter, buyer, alice);
3047 env(token::setMinter(issuer, minter));
3052 env(token::mint(minter, 0),
3053 token::issuer(issuer),
3063 env(token::createOffer(minter, nftokenID, drops(1)),
3064 token::destination(buyer),
3082 env(token::createOffer(minter, nftokenID, drops(1)),
3083 token::destination(buyer),
3087 env(token::cancelOffer(minter, {offerIndex}));
3096 env(token::createOffer(minter, nftokenID, drops(1)),
3097 token::destination(buyer),
3104 env(token::cancelOffer(minter, {offerIndex}));
3116 env(token::createOffer(minter, nftokenID, drops(1)),
3117 token::destination(buyer),
3121 env(token::acceptSellOffer(buyer, offerIndex));
3133 env(token::createOffer(alice, nftokenID, drops(1)),
3134 token::owner(buyer),
3141 env(token::createOffer(minter, nftokenID, drops(1)),
3142 token::owner(buyer),
3152 testcase(
"Create offer expiration");
3154 using namespace test::jtx;
3156 Env env{*
this, features};
3158 Account
const issuer{
"issuer"};
3159 Account
const minter{
"minter"};
3160 Account
const buyer{
"buyer"};
3162 env.fund(XRP(1000), issuer, minter, buyer);
3166 env(token::setMinter(issuer, minter));
3171 env(token::mint(minter, 0),
3172 token::issuer(issuer),
3178 env(token::mint(minter, 0),
3179 token::issuer(issuer),
3188 uint256 const offerMinterToIssuer =
3190 env(token::createOffer(minter, nftokenID0, drops(1)),
3191 token::destination(issuer),
3192 token::expiration(expiration),
3195 uint256 const offerMinterToAnyone =
3197 env(token::createOffer(minter, nftokenID0, drops(1)),
3198 token::expiration(expiration),
3201 uint256 const offerIssuerToMinter =
3203 env(token::createOffer(issuer, nftokenID0, drops(1)),
3204 token::owner(minter),
3205 token::expiration(expiration));
3207 uint256 const offerBuyerToMinter =
3209 env(token::createOffer(buyer, nftokenID0, drops(1)),
3210 token::owner(minter),
3211 token::expiration(expiration));
3223 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3225 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3228 BEAST_EXPECT(
lastClose(env) < expiration);
3234 env(token::cancelOffer(minter, {offerMinterToAnyone}));
3238 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3249 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3250 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3265 env(token::createOffer(minter, nftokenID0, drops(1)),
3266 token::expiration(expiration),
3271 env(token::createOffer(minter, nftokenID1, drops(1)),
3272 token::expiration(expiration),
3275 BEAST_EXPECT(
lastClose(env) < expiration);
3281 env(token::acceptSellOffer(buyer, offer0));
3292 env(token::acceptSellOffer(buyer, offer1), ter(
tecEXPIRED));
3293 env(token::acceptSellOffer(issuer, offer1), ter(
tecEXPIRED));
3302 env(token::cancelOffer(issuer, {offer1}));
3312 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3314 token::destination(minter));
3316 env(token::acceptSellOffer(minter, offerSellBack));
3330 env(token::createOffer(buyer, nftokenID0, drops(1)),
3331 token::owner(minter),
3332 token::expiration(expiration));
3335 env(token::createOffer(buyer, nftokenID1, drops(1)),
3336 token::owner(minter),
3337 token::expiration(expiration));
3339 BEAST_EXPECT(
lastClose(env) < expiration);
3345 env(token::acceptBuyOffer(minter, offer0));
3356 env(token::acceptBuyOffer(minter, offer1), ter(
tecEXPIRED));
3357 env(token::acceptBuyOffer(issuer, offer1), ter(
tecEXPIRED));
3366 env(token::cancelOffer(issuer, {offer1}));
3376 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3378 token::destination(minter));
3380 env(token::acceptSellOffer(minter, offerSellBack));
3395 env(token::createOffer(minter, nftokenID0, drops(1)),
3396 token::expiration(expiration),
3401 env(token::createOffer(minter, nftokenID1, drops(1)),
3402 token::expiration(expiration),
3407 env(token::createOffer(buyer, nftokenID0, drops(1)),
3408 token::owner(minter));
3412 env(token::createOffer(buyer, nftokenID1, drops(1)),
3413 token::owner(minter));
3416 BEAST_EXPECT(
lastClose(env) < expiration);
3422 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3433 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3443 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3453 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3455 token::destination(minter));
3457 env(token::acceptSellOffer(minter, offerSellBack));
3472 env(token::createOffer(minter, nftokenID0, drops(1)),
3477 env(token::createOffer(minter, nftokenID1, drops(1)),
3482 env(token::createOffer(buyer, nftokenID0, drops(1)),
3483 token::expiration(expiration),
3484 token::owner(minter));
3488 env(token::createOffer(buyer, nftokenID1, drops(1)),
3489 token::expiration(expiration),
3490 token::owner(minter));
3493 BEAST_EXPECT(
lastClose(env) < expiration);
3499 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3510 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3520 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3530 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3532 token::destination(minter));
3534 env(token::acceptSellOffer(minter, offerSellBack));
3550 env(token::createOffer(minter, nftokenID0, drops(1)),
3551 token::expiration(expiration),
3556 env(token::createOffer(minter, nftokenID1, drops(1)),
3557 token::expiration(expiration),
3562 env(token::createOffer(buyer, nftokenID0, drops(1)),
3563 token::expiration(expiration),
3564 token::owner(minter));
3568 env(token::createOffer(buyer, nftokenID1, drops(1)),
3569 token::expiration(expiration),
3570 token::owner(minter));
3573 BEAST_EXPECT(
lastClose(env) < expiration);
3579 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3590 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3600 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3610 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3612 token::destination(minter));
3614 env(token::acceptSellOffer(minter, offerSellBack));
3626 testcase(
"Cancel offers");
3628 using namespace test::jtx;
3630 Env env{*
this, features};
3632 Account
const alice(
"alice");
3633 Account
const becky(
"becky");
3634 Account
const minter(
"minter");
3635 env.fund(XRP(50000), alice, becky, minter);
3639 env(token::setMinter(alice, minter));
3648 uint256 const expiredOfferIndex =
3651 env(token::createOffer(alice, nftokenID, XRP(1000)),
3653 token::expiration(
lastClose(env) + 13));
3658 env(token::cancelOffer(becky, {expiredOfferIndex}),
3666 env(token::cancelOffer(becky, {expiredOfferIndex}));
3672 uint256 const dest1OfferIndex =
3675 env(token::createOffer(alice, nftokenID, XRP(1000)),
3676 token::destination(becky),
3682 env(token::cancelOffer(minter, {dest1OfferIndex}),
3687 env(token::cancelOffer(becky, {dest1OfferIndex}));
3692 uint256 const dest2OfferIndex =
3695 env(token::createOffer(alice, nftokenID, XRP(1000)),
3696 token::destination(becky),
3701 env(token::cancelOffer(alice, {dest2OfferIndex}));
3708 uint256 const mintersNFTokenID =
3710 env(token::mint(minter, 0),
3711 token::issuer(alice),
3715 uint256 const minterOfferIndex =
3718 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3724 env(token::cancelOffer(alice, {minterOfferIndex}),
3726 env(token::cancelOffer(becky, {minterOfferIndex}),
3731 env(token::cancelOffer(minter, {minterOfferIndex}));
3740 testcase(
"Cancel too many offers");
3742 using namespace test::jtx;
3744 Env env{*
this, features};
3759 Account
const alice(
"alice");
3760 env.fund(XRP(1000), alice);
3769 Account
const offerAcct(
3771 env.fund(XRP(1000), nftAcct, offerAcct);
3776 env(token::mint(nftAcct, 0),
3783 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3784 token::owner(nftAcct),
3793 for (
uint256 const& offerIndex : offerIndexes)
3800 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3804 env(token::cancelOffer(alice, {offerIndexes.back()}));
3809 offerIndexes.pop_back();
3815 env(token::mint(alice, 0),
3821 env(token::createOffer(alice, nftokenID, drops(1)),
3830 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3834 env(token::burn(alice, nftokenID));
3840 offerIndexes.pop_back();
3845 env(token::cancelOffer(alice, offerIndexes));
3849 for (
uint256 const& offerIndex : offerIndexes)
3859 testcase(
"Brokered NFT offer accept");
3861 using namespace test::jtx;
3863 for (
auto const& tweakedFeatures :
3867 Env env{*
this, tweakedFeatures};
3875 Account
const issuer{
"issuer"};
3876 Account
const minter{
"minter"};
3877 Account
const buyer{
"buyer"};
3878 Account
const broker{
"broker"};
3879 Account
const gw{
"gw"};
3880 IOU
const gwXAU(gw[
"XAU"]);
3882 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3885 env(trust(issuer, gwXAU(2000)));
3886 env(trust(minter, gwXAU(2000)));
3887 env(trust(buyer, gwXAU(2000)));
3888 env(trust(broker, gwXAU(2000)));
3891 env(token::setMinter(issuer, minter));
3895 auto checkOwnerCountIsOne =
3900 for (Account
const& acct : accounts)
3907 ss <<
"Account " << acct.human()
3908 <<
" expected ownerCount == 1. Got "
3910 fail(ss.
str(), __FILE__, line);
3916 auto mintNFT = [&env, &issuer, &minter](
std::uint16_t xferFee = 0) {
3919 env(token::mint(minter, 0),
3920 token::issuer(issuer),
3921 token::xferFee(xferFee),
3933 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3935 uint256 const nftID = mintNFT();
3938 uint256 const minterOfferIndex =
3940 env(token::createOffer(minter, nftID, XRP(0)),
3948 env(token::createOffer(buyer, nftID, XRP(1)),
3949 token::owner(minter));
3952 auto const minterBalance = env.balance(minter);
3953 auto const buyerBalance = env.balance(buyer);
3954 auto const brokerBalance = env.balance(broker);
3955 auto const issuerBalance = env.balance(issuer);
3958 env(token::brokerOffers(
3959 broker, buyOfferIndex, minterOfferIndex));
3964 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3965 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3966 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3967 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3970 env(token::burn(buyer, nftID));
3980 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3982 uint256 const nftID = mintNFT();
3985 uint256 const minterOfferIndex =
3987 env(token::createOffer(minter, nftID, XRP(0)),
3995 env(token::createOffer(buyer, nftID, XRP(1)),
3996 token::owner(minter));
4000 env(token::brokerOffers(
4001 broker, buyOfferIndex, minterOfferIndex),
4002 token::brokerFee(XRP(1.1)),
4006 auto const minterBalance = env.balance(minter);
4007 auto const buyerBalance = env.balance(buyer);
4008 auto const brokerBalance = env.balance(broker);
4009 auto const issuerBalance = env.balance(issuer);
4012 env(token::brokerOffers(
4013 broker, buyOfferIndex, minterOfferIndex),
4014 token::brokerFee(XRP(0.5)));
4019 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4020 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4022 env.balance(broker) ==
4023 brokerBalance + XRP(0.5) - drops(10));
4024 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
4027 env(token::burn(buyer, nftID));
4037 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4042 uint256 const minterOfferIndex =
4044 env(token::createOffer(minter, nftID, XRP(0)),
4052 env(token::createOffer(buyer, nftID, XRP(1)),
4053 token::owner(minter));
4056 auto const minterBalance = env.balance(minter);
4057 auto const buyerBalance = env.balance(buyer);
4058 auto const brokerBalance = env.balance(broker);
4059 auto const issuerBalance = env.balance(issuer);
4062 env(token::brokerOffers(
4063 broker, buyOfferIndex, minterOfferIndex));
4068 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4069 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4070 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
4071 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
4074 env(token::burn(buyer, nftID));
4084 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4089 uint256 const minterOfferIndex =
4091 env(token::createOffer(minter, nftID, XRP(0)),
4099 env(token::createOffer(buyer, nftID, XRP(1)),
4100 token::owner(minter));
4103 auto const minterBalance = env.balance(minter);
4104 auto const buyerBalance = env.balance(buyer);
4105 auto const brokerBalance = env.balance(broker);
4106 auto const issuerBalance = env.balance(issuer);
4109 env(token::brokerOffers(
4110 broker, buyOfferIndex, minterOfferIndex),
4111 token::brokerFee(XRP(0.75)));
4117 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
4118 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4120 env.balance(broker) ==
4121 brokerBalance + XRP(0.75) - drops(10));
4122 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
4125 env(token::burn(buyer, nftID));
4131 auto setXAUBalance =
4132 [
this, &gw, &gwXAU, &env](
4137 for (Account
const& acct : accounts)
4139 auto const xauAmt = gwXAU(amount);
4140 auto const balance = env.balance(acct, gwXAU);
4141 if (balance < xauAmt)
4143 env(pay(gw, acct, xauAmt - balance));
4146 else if (balance > xauAmt)
4148 env(pay(acct, gw, balance - xauAmt));
4151 if (env.balance(acct, gwXAU) != xauAmt)
4154 ss <<
"Unable to set " << acct.human()
4155 <<
" account balance to gwXAU(" << amount <<
")";
4156 this->fail(ss.
str(), __FILE__, line);
4164 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4165 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4167 uint256 const nftID = mintNFT();
4170 uint256 const minterOfferIndex =
4172 env(token::createOffer(minter, nftID, gwXAU(1000)),
4181 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4182 token::owner(minter));
4186 env(token::brokerOffers(
4187 broker, buyOfferIndex, minterOfferIndex),
4193 env(token::cancelOffer(buyer, {buyOfferIndex}));
4201 env(token::createOffer(buyer, nftID, gwXAU(999)),
4202 token::owner(minter));
4206 env(token::brokerOffers(
4207 broker, buyOfferIndex, minterOfferIndex),
4213 env(token::cancelOffer(buyer, {buyOfferIndex}));
4220 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4221 token::owner(minter));
4225 env(token::brokerOffers(
4226 broker, buyOfferIndex, minterOfferIndex),
4227 token::brokerFee(gwXAU(0.1)),
4232 env(token::brokerOffers(
4233 broker, buyOfferIndex, minterOfferIndex));
4240 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4241 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4242 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4243 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4246 env(token::burn(buyer, nftID));
4253 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4254 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4259 uint256 const minterOfferIndex =
4261 env(token::createOffer(minter, nftID, gwXAU(900)),
4269 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4270 token::owner(minter));
4274 env(token::brokerOffers(
4275 broker, buyOfferIndex, minterOfferIndex),
4281 env(token::cancelOffer(buyer, {buyOfferIndex}));
4289 env(token::createOffer(buyer, nftID, gwXAU(899)),
4290 token::owner(minter));
4294 env(token::brokerOffers(
4295 broker, buyOfferIndex, minterOfferIndex),
4301 env(token::cancelOffer(buyer, {buyOfferIndex}));
4307 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4308 token::owner(minter));
4313 env(token::brokerOffers(
4314 broker, buyOfferIndex, minterOfferIndex),
4315 token::brokerFee(gwXAU(101)),
4321 env(token::brokerOffers(
4322 broker, buyOfferIndex, minterOfferIndex),
4323 token::brokerFee(gwXAU(100)));
4330 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4331 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4332 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4333 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4336 env(token::burn(buyer, nftID));
4343 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4344 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4349 uint256 const minterOfferIndex =
4351 env(token::createOffer(minter, nftID, gwXAU(900)),
4358 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4359 token::owner(minter));
4365 env(token::brokerOffers(
4366 broker, buyOfferIndex, minterOfferIndex),
4367 token::brokerFee(gwXAU(50)));
4374 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4375 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4376 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4377 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4380 env(token::burn(buyer, nftID));
4385 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4386 setXAUBalance({issuer, minter, buyer}, 1000, __LINE__);
4387 setXAUBalance({broker}, 500, __LINE__);
4391 uint256 const minterOfferIndex =
4393 env(token::createOffer(minter, nftID, gwXAU(900)),
4400 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4401 token::owner(minter));
4406 env(token::brokerOffers(
4407 broker, buyOfferIndex, minterOfferIndex),
4408 token::brokerFee(gwXAU(50)));
4414 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4415 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4416 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4417 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(550));
4420 env(token::burn(buyer, nftID));
4425 env(token::brokerOffers(
4426 broker, buyOfferIndex, minterOfferIndex),
4427 token::brokerFee(gwXAU(50)),
4434 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4435 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
4436 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000));
4437 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(500));
4440 env(token::burn(minter, nftID));
4451 testcase(
"NFToken offer owner");
4453 using namespace test::jtx;
4455 Env env{*
this, features};
4457 Account
const issuer{
"issuer"};
4458 Account
const buyer1{
"buyer1"};
4459 Account
const buyer2{
"buyer2"};
4460 env.fund(XRP(10000), issuer, buyer1, buyer2);
4469 BEAST_EXPECT(
nftCount(env, issuer) == 1);
4470 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4471 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4474 uint256 const buyer1OfferIndex =
4476 env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4477 uint256 const buyer2OfferIndex =
4479 env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4489 env.rpc(
"json",
"nft_buy_offers",
to_string(params));
4491 if (buyOffers.
isMember(jss::result) &&
4492 buyOffers[jss::result].
isMember(jss::offers))
4493 return buyOffers[jss::result][jss::offers].
size();
4499 BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4502 env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4506 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4507 BEAST_EXPECT(
nftCount(env, buyer1) == 1);
4508 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4512 BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4516 env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4520 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4521 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4522 BEAST_EXPECT(
nftCount(env, buyer2) == 1);
4525 BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4532 testcase(
"NFToken transactions with tickets");
4534 using namespace test::jtx;
4536 Env env{*
this, features};
4538 Account
const issuer{
"issuer"};
4539 Account
const buyer{
"buyer"};
4540 env.fund(XRP(10000), issuer, buyer);
4547 env(ticket::create(issuer, 10));
4553 env(ticket::create(buyer, 10));
4561 env(token::mint(issuer, 0u),
4563 ticket::use(issuerTicketSeq++));
4571 env(token::createOffer(buyer, nftId, XRP(1)),
4572 token::owner(issuer),
4573 ticket::use(buyerTicketSeq++));
4579 env(token::cancelOffer(buyer, {offerIndex0}),
4580 ticket::use(buyerTicketSeq++));
4587 env(token::createOffer(buyer, nftId, XRP(2)),
4588 token::owner(issuer),
4589 ticket::use(buyerTicketSeq++));
4595 env(token::acceptBuyOffer(issuer, offerIndex1),
4596 ticket::use(issuerTicketSeq++));
4603 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4610 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4611 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4622 testcase(
"NFToken delete account");
4624 using namespace test::jtx;
4626 Env env{*
this, features};
4628 Account
const issuer{
"issuer"};
4629 Account
const minter{
"minter"};
4630 Account
const becky{
"becky"};
4631 Account
const carla{
"carla"};
4632 Account
const daria{
"daria"};
4634 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4638 for (
int i = 0; i < 300; ++i)
4641 env(token::setMinter(issuer, minter));
4645 env(token::mint(minter, 0u),
4646 token::issuer(issuer),
4659 for (
int i = 0; i < 15; ++i)
4663 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4666 uint256 const carlaOfferIndex =
4668 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4673 env(acctdelete(becky, daria), fee(XRP(50)));
4677 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4682 env(acctdelete(minter, daria), fee(XRP(50)));
4694 for (
int i = 0; i < 15; ++i)
4699 env(token::burn(carla, nftId));
4702 env(acctdelete(issuer, daria), fee(XRP(50)));
4703 env(acctdelete(carla, daria), fee(XRP(50)));
4710 testcase(
"nft_buy_offers and nft_sell_offers");
4719 using namespace test::jtx;
4721 Env env{*
this, features};
4723 Account
const issuer{
"issuer"};
4724 Account
const buyer{
"buyer"};
4727 env.fund(XRP(1000000), issuer, buyer);
4736 auto checkOffers = [
this, &env, &nftID](
4737 char const* request,
4739 int expectMarkerCount,
4741 int markerCount = 0;
4748 Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4752 if (!marker.
empty())
4753 params[jss::marker] = marker;
4754 return env.rpc(
"json", request,
to_string(params));
4758 if (expectCount == 0)
4762 "expected \"result\"",
4767 nftOffers[jss::result].isMember(jss::error),
4768 "expected \"error\"",
4773 nftOffers[jss::result][jss::error].asString() ==
4775 "expected \"objectNotFound\"",
4786 "expected \"result\"",
4795 marker = result[jss::marker].
asString();
4800 "expected \"offers\"",
4806 allOffers.
append(someOffers[i]);
4809 }
while (!marker.
empty());
4813 allOffers.
size() == expectCount,
4814 "Unexpected returned offer count",
4818 markerCount == expectMarkerCount,
4819 "Unexpected marker count",
4829 globalFlags = offer[jss::flags].asInt();
4832 *globalFlags == offer[jss::flags].asInt(),
4833 "Inconsistent flags returned",
4839 offerIndexes.
insert(offer[jss::nft_offer_index].asString());
4840 amounts.
insert(offer[jss::amount].asString());
4844 offerIndexes.
size() == expectCount,
4845 "Duplicate indexes returned?",
4849 amounts.
size() == expectCount,
4850 "Duplicate amounts returned?",
4856 checkOffers(
"nft_sell_offers", 0,
false, __LINE__);
4860 auto makeSellOffers =
4861 [&env, &issuer, &nftID, &sellPrice](
STAmount const& limit) {
4864 while (sellPrice < limit)
4866 sellPrice += XRP(1);
4867 env(token::createOffer(issuer, nftID, sellPrice),
4869 if (++offerCount % 10 == 0)
4876 makeSellOffers(XRP(1));
4877 checkOffers(
"nft_sell_offers", 1, 0, __LINE__);
4880 makeSellOffers(XRP(250));
4881 checkOffers(
"nft_sell_offers", 250, 0, __LINE__);
4884 makeSellOffers(XRP(251));
4885 checkOffers(
"nft_sell_offers", 251, 1, __LINE__);
4888 makeSellOffers(XRP(500));
4889 checkOffers(
"nft_sell_offers", 500, 1, __LINE__);
4892 makeSellOffers(XRP(501));
4893 checkOffers(
"nft_sell_offers", 501, 2, __LINE__);
4896 checkOffers(
"nft_buy_offers", 0, 0, __LINE__);
4900 auto makeBuyOffers =
4901 [&env, &buyer, &issuer, &nftID, &buyPrice](
STAmount const& limit) {
4904 while (buyPrice < limit)
4907 env(token::createOffer(buyer, nftID, buyPrice),
4908 token::owner(issuer));
4909 if (++offerCount % 10 == 0)
4916 makeBuyOffers(XRP(1));
4917 checkOffers(
"nft_buy_offers", 1, 0, __LINE__);
4920 makeBuyOffers(XRP(250));
4921 checkOffers(
"nft_buy_offers", 250, 0, __LINE__);
4924 makeBuyOffers(XRP(251));
4925 checkOffers(
"nft_buy_offers", 251, 1, __LINE__);
4928 makeBuyOffers(XRP(500));
4929 checkOffers(
"nft_buy_offers", 500, 1, __LINE__);
4932 makeBuyOffers(XRP(501));
4933 checkOffers(
"nft_buy_offers", 501, 2, __LINE__);
4940 using namespace test::jtx;
4942 testcase(
"fixNFTokenNegOffer");
4944 Account
const issuer{
"issuer"};
4945 Account
const buyer{
"buyer"};
4946 Account
const gw{
"gw"};
4947 IOU
const gwXAU(gw[
"XAU"]);
4953 for (
auto const& tweakedFeatures :
4962 Env env{*
this, tweakedFeatures};
4964 env.fund(XRP(1000000), issuer, buyer, gw);
4967 env(trust(issuer, gwXAU(2000)));
4968 env(trust(buyer, gwXAU(2000)));
4971 env(pay(gw, issuer, gwXAU(1000)));
4972 env(pay(gw, buyer, gwXAU(1000)));
4992 uint256 const sellNegXrpOfferIndex =
4994 env(token::createOffer(issuer, nftID0, XRP(-2)),
4996 ter(offerCreateTER));
4999 uint256 const sellNegIouOfferIndex =
5001 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
5003 ter(offerCreateTER));
5006 uint256 const buyNegXrpOfferIndex =
5008 env(token::createOffer(buyer, nftID0, XRP(-1)),
5009 token::owner(issuer),
5010 ter(offerCreateTER));
5013 uint256 const buyNegIouOfferIndex =
5015 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
5016 token::owner(issuer),
5017 ter(offerCreateTER));
5029 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
5030 ter(offerAcceptTER));
5032 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
5033 ter(offerAcceptTER));
5037 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5038 ter(offerAcceptTER));
5040 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5041 ter(offerAcceptTER));
5054 env(token::brokerOffers(
5055 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5056 ter(offerAcceptTER));
5058 env(token::brokerOffers(
5059 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5060 ter(offerAcceptTER));
5072 env.fund(XRP(1000000), issuer, buyer, gw);
5075 env(trust(issuer, gwXAU(2000)));
5076 env(trust(buyer, gwXAU(2000)));
5079 env(pay(gw, issuer, gwXAU(1000)));
5080 env(pay(gw, buyer, gwXAU(1000)));
5096 uint256 const sellNegXrpOfferIndex =
5098 env(token::createOffer(issuer, nftID0, XRP(-2)),
5102 uint256 const sellNegIouOfferIndex =
5104 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
5108 uint256 const buyNegXrpOfferIndex =
5110 env(token::createOffer(buyer, nftID0, XRP(-1)),
5111 token::owner(issuer));
5114 uint256 const buyNegIouOfferIndex =
5116 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
5117 token::owner(issuer));
5126 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
5129 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
5134 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5137 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5142 env(token::brokerOffers(
5143 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5146 env(token::brokerOffers(
5147 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5154 for (
auto const& tweakedFeatures :
5158 Env env{*
this, tweakedFeatures};
5160 env.fund(XRP(1000000), issuer, buyer);
5172 env(token::createOffer(buyer, nftID, drops(1)),
5173 token::owner(issuer),
5174 token::destination(issuer),
5175 ter(offerCreateTER));
5183 using namespace test::jtx;
5185 testcase(
"Payments with IOU transfer fees");
5187 for (
auto const& tweakedFeatures :
5191 Env env{*
this, tweakedFeatures};
5193 Account
const minter{
"minter"};
5194 Account
const secondarySeller{
"seller"};
5195 Account
const buyer{
"buyer"};
5196 Account
const gw{
"gateway"};
5197 Account
const broker{
"broker"};
5198 IOU
const gwXAU(gw[
"XAU"]);
5199 IOU
const gwXPB(gw[
"XPB"]);
5201 env.fund(XRP(1000), gw, minter, secondarySeller, buyer, broker);
5204 env(trust(minter, gwXAU(2000)));
5205 env(trust(secondarySeller, gwXAU(2000)));
5206 env(trust(broker, gwXAU(10000)));
5207 env(trust(buyer, gwXAU(2000)));
5208 env(trust(buyer, gwXPB(2000)));
5212 env(rate(gw, 1.02));
5215 auto expectInitialState = [
this,
5228 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000));
5229 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(0));
5230 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(0));
5231 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(0));
5232 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(0));
5233 BEAST_EXPECT(env.balance(secondarySeller, gwXPB) == gwXPB(0));
5234 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5000));
5235 BEAST_EXPECT(env.balance(broker, gwXPB) == gwXPB(0));
5236 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-1000));
5237 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(0));
5238 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(0));
5239 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(0));
5241 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(0));
5243 env.balance(gw, secondarySeller[
"XPB"]) == gwXPB(0));
5244 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5000));
5245 BEAST_EXPECT(env.balance(gw, broker[
"XPB"]) == gwXPB(0));
5248 auto reinitializeTrustLineBalances = [&expectInitialState,
5257 if (
auto const difference =
5258 gwXAU(1000) - env.balance(buyer, gwXAU);
5259 difference > gwXAU(0))
5260 env(pay(gw, buyer, difference));
5261 if (env.balance(buyer, gwXPB) > gwXPB(0))
5262 env(pay(buyer, gw, env.balance(buyer, gwXPB)));
5263 if (env.balance(minter, gwXAU) > gwXAU(0))
5264 env(pay(minter, gw, env.balance(minter, gwXAU)));
5265 if (env.balance(minter, gwXPB) > gwXPB(0))
5266 env(pay(minter, gw, env.balance(minter, gwXPB)));
5267 if (env.balance(secondarySeller, gwXAU) > gwXAU(0))
5269 pay(secondarySeller,
5271 env.balance(secondarySeller, gwXAU)));
5272 if (env.balance(secondarySeller, gwXPB) > gwXPB(0))
5274 pay(secondarySeller,
5276 env.balance(secondarySeller, gwXPB)));
5277 auto brokerDiff = gwXAU(5000) - env.balance(broker, gwXAU);
5278 if (brokerDiff > gwXAU(0))
5279 env(pay(gw, broker, brokerDiff));
5280 else if (brokerDiff < gwXAU(0))
5282 brokerDiff.negate();
5283 env(pay(broker, gw, brokerDiff));
5285 if (env.balance(broker, gwXPB) > gwXPB(0))
5286 env(pay(broker, gw, env.balance(broker, gwXPB)));
5288 expectInitialState();
5291 auto mintNFT = [&env](Account
const& minter,
int transferFee = 0) {
5292 uint256 const nftID = token::getNextID(
5294 env(token::mint(minter),
5295 token::xferFee(transferFee),
5301 auto createBuyOffer =
5303 Account
const& offerer,
5304 Account
const& owner,
5310 env(token::createOffer(offerer, nftID, amount),
5311 token::owner(owner),
5312 terCode ? ter(*terCode)
5318 auto createSellOffer =
5320 Account
const& offerer,
5326 env(token::createOffer(offerer, nftID, amount),
5328 terCode ? ter(*terCode)
5337 reinitializeTrustLineBalances();
5338 auto const nftID = mintNFT(minter);
5339 auto const offerID =
5340 createSellOffer(minter, nftID, gwXAU(1000));
5344 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5348 expectInitialState();
5351 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5352 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5354 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5355 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5361 reinitializeTrustLineBalances();
5362 auto const nftID = mintNFT(minter);
5363 auto const offerID =
5364 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5368 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5372 expectInitialState();
5375 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5376 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5378 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5379 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5386 reinitializeTrustLineBalances();
5387 auto const nftID = mintNFT(minter);
5388 auto const offerID = createSellOffer(minter, nftID, gwXAU(995));
5392 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5396 expectInitialState();
5399 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(995));
5400 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-14.9));
5401 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-995));
5402 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(14.9));
5409 reinitializeTrustLineBalances();
5410 auto const nftID = mintNFT(minter);
5411 auto const offerID =
5412 createBuyOffer(buyer, minter, nftID, gwXAU(995));
5416 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5420 expectInitialState();
5423 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(995));
5424 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-14.9));
5425 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-995));
5426 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(14.9));
5434 reinitializeTrustLineBalances();
5435 auto const nftID = mintNFT(minter);
5436 auto const offerID = createSellOffer(minter, nftID, gwXAU(900));
5437 env(token::acceptSellOffer(buyer, offerID));
5440 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5441 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5442 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-900));
5443 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5450 reinitializeTrustLineBalances();
5451 auto const nftID = mintNFT(minter);
5452 auto const offerID =
5453 createBuyOffer(buyer, minter, nftID, gwXAU(900));
5454 env(token::acceptBuyOffer(minter, offerID));
5457 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5458 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5459 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-900));
5460 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5467 reinitializeTrustLineBalances();
5470 env(pay(gw, buyer, gwXAU(20)));
5473 auto const nftID = mintNFT(minter);
5474 auto const offerID =
5475 createSellOffer(minter, nftID, gwXAU(1000));
5476 env(token::acceptSellOffer(buyer, offerID));
5479 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5480 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5481 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5482 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5489 reinitializeTrustLineBalances();
5492 env(pay(gw, buyer, gwXAU(20)));
5495 auto const nftID = mintNFT(minter);
5496 auto const offerID =
5497 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5498 env(token::acceptBuyOffer(minter, offerID));
5501 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5502 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5503 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5504 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5509 reinitializeTrustLineBalances();
5511 auto const nftID = mintNFT(minter);
5512 auto const offerID =
5513 createSellOffer(minter, nftID, gwXAU(1000));
5517 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5522 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5524 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5527 expectInitialState();
5532 reinitializeTrustLineBalances();
5534 auto const nftID = mintNFT(minter);
5538 auto const offerID =
5539 createBuyOffer(gw, minter, nftID, gwXAU(1000), {offerTER});
5543 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5548 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5550 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5553 expectInitialState();
5558 reinitializeTrustLineBalances();
5559 auto const nftID = mintNFT(minter);
5560 auto const offerID =
5561 createSellOffer(minter, nftID, gwXAU(5000));
5565 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5570 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5572 env.balance(gw, minter[
"XAU"]) == gwXAU(-5000));
5575 expectInitialState();
5580 reinitializeTrustLineBalances();
5582 auto const nftID = mintNFT(minter);
5586 auto const offerID =
5587 createBuyOffer(gw, minter, nftID, gwXAU(5000), {offerTER});
5591 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5596 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5598 env.balance(gw, minter[
"XAU"]) == gwXAU(-5000));
5601 expectInitialState();
5607 reinitializeTrustLineBalances();
5608 auto const nftID = mintNFT(gw);
5609 auto const offerID = createSellOffer(gw, nftID, gwXAU(1000));
5610 env(token::acceptSellOffer(buyer, offerID));
5613 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5614 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5620 reinitializeTrustLineBalances();
5622 auto const nftID = mintNFT(gw);
5623 auto const offerID =
5624 createBuyOffer(buyer, gw, nftID, gwXAU(1000));
5625 env(token::acceptBuyOffer(gw, offerID));
5628 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5629 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5635 reinitializeTrustLineBalances();
5636 auto const nftID = mintNFT(gw);
5637 auto const offerID = createSellOffer(gw, nftID, gwXAU(2000));
5638 env(token::acceptSellOffer(buyer, offerID),
5641 expectInitialState();
5647 reinitializeTrustLineBalances();
5648 auto const nftID = mintNFT(gw);
5649 auto const offerID =
5650 createBuyOffer(buyer, gw, nftID, gwXAU(2000));
5651 env(token::acceptBuyOffer(gw, offerID),
5654 expectInitialState();
5659 reinitializeTrustLineBalances();
5660 auto const nftID = mintNFT(minter);
5661 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5662 env(token::acceptSellOffer(buyer, offerID),
5665 expectInitialState();
5670 reinitializeTrustLineBalances();
5671 auto const nftID = mintNFT(minter);
5672 auto const offerID = createBuyOffer(
5678 env(token::acceptBuyOffer(minter, offerID),
5681 expectInitialState();
5687 reinitializeTrustLineBalances();
5688 env(pay(gw, buyer, gwXPB(100)));
5691 auto const nftID = mintNFT(minter);
5692 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5693 env(token::acceptSellOffer(buyer, offerID));
5696 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5697 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5698 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(-10));
5699 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(-89.8));
5705 reinitializeTrustLineBalances();
5706 env(pay(gw, buyer, gwXPB(100)));
5709 auto const nftID = mintNFT(minter);
5710 auto const offerID =
5711 createBuyOffer(buyer, minter, nftID, gwXPB(10));
5712 env(token::acceptBuyOffer(minter, offerID));
5715 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5716 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5717 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(-10));
5718 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(-89.8));
5723 reinitializeTrustLineBalances();
5727 auto const nftID = mintNFT(minter, 3000);
5728 auto const primaryOfferID =
5729 createSellOffer(minter, nftID, XRP(0));
5730 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5734 auto const offerID =
5735 createSellOffer(secondarySeller, nftID, gwXAU(1000));
5739 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5743 expectInitialState();
5746 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(30));
5748 env.balance(secondarySeller, gwXAU) == gwXAU(970));
5749 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5750 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-30));
5752 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-970));
5753 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5759 reinitializeTrustLineBalances();
5763 auto const nftID = mintNFT(minter, 3000);
5764 auto const primaryOfferID =
5765 createSellOffer(minter, nftID, XRP(0));
5766 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5770 auto const offerID =
5771 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(1000));
5775 env(token::acceptBuyOffer(secondarySeller, offerID),
5780 expectInitialState();
5783 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(30));
5785 env.balance(secondarySeller, gwXAU) == gwXAU(970));
5786 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5787 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-30));
5789 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-970));
5790 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5796 reinitializeTrustLineBalances();
5800 auto const nftID = mintNFT(minter, 3000);
5801 auto const primaryOfferID =
5802 createSellOffer(minter, nftID, XRP(0));
5803 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5807 auto const offerID =
5808 createSellOffer(secondarySeller, nftID, gwXAU(900));
5809 env(token::acceptSellOffer(buyer, offerID));
5812 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5813 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5814 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5815 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-27));
5817 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-873));
5818 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5823 reinitializeTrustLineBalances();
5827 auto const nftID = mintNFT(minter, 3000);
5828 auto const primaryOfferID =
5829 createSellOffer(minter, nftID, XRP(0));
5830 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5834 auto const offerID =
5835 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(900));
5836 env(token::acceptBuyOffer(secondarySeller, offerID));
5840 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5842 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5844 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5845 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-27));
5847 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-873));
5848 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5866 reinitializeTrustLineBalances();
5868 auto const nftID = mintNFT(minter);
5869 auto const sellOffer =
5870 createSellOffer(minter, nftID, gwXAU(300));
5871 auto const buyOffer =
5872 createBuyOffer(buyer, minter, nftID, gwXAU(500));
5873 env(token::brokerOffers(broker, buyOffer, sellOffer),
5874 token::brokerFee(gwXAU(100)));
5877 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(400));
5878 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5879 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5880 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-400));
5881 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-490));
5882 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5100));
5900 reinitializeTrustLineBalances();
5904 auto const nftID = mintNFT(minter, 3000);
5905 auto const primaryOfferID =
5906 createSellOffer(minter, nftID, XRP(0));
5907 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5911 auto const sellOffer =
5912 createSellOffer(secondarySeller, nftID, gwXAU(300));
5913 auto const buyOffer =
5914 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(500));
5915 env(token::brokerOffers(broker, buyOffer, sellOffer),
5916 token::brokerFee(gwXAU(100)));
5919 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(12));
5920 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5921 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(388));
5922 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5923 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-12));
5924 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-490));
5926 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-388));
5927 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5100));
5947 testcase(
"Brokered sale to self");
5949 using namespace test::jtx;
5951 Account
const alice{
"alice"};
5952 Account
const bob{
"bob"};
5953 Account
const broker{
"broker"};
5955 Env env{*
this, features};
5956 env.fund(XRP(10000), alice, bob, broker);
5982 uint256 const bobBuyOfferIndex =
5984 env(token::createOffer(bob, nftId, XRP(5)), token::owner(alice));
5986 uint256 const aliceSellOfferIndex =
5988 env(token::createOffer(alice, nftId, XRP(0)),
5989 token::destination(bob),
5994 env(token::acceptSellOffer(bob, aliceSellOfferIndex));
6001 uint256 const bobSellOfferIndex =
6003 env(token::createOffer(bob, nftId, XRP(4)), txflags(
tfSellNFToken));
6008 BEAST_EXPECT(
nftCount(env, bob) == 1);
6009 auto const bobsPriorBalance = env.balance(bob);
6010 auto const brokersPriorBalance = env.balance(broker);
6014 env(token::brokerOffers(broker, bobBuyOfferIndex, bobSellOfferIndex),
6015 token::brokerFee(XRP(1)),
6024 BEAST_EXPECT(
nftCount(env, bob) == 1);
6025 BEAST_EXPECT(env.balance(bob) == bobsPriorBalance - XRP(1));
6027 env.balance(broker) ==
6028 brokersPriorBalance + XRP(1) - drops(10));
6034 BEAST_EXPECT(
nftCount(env, bob) == 1);
6035 BEAST_EXPECT(env.balance(bob) == bobsPriorBalance);
6037 env.balance(broker) == brokersPriorBalance - drops(10));
6078 using namespace test::jtx;