rippled
Loading...
Searching...
No Matches
NFToken_test.cpp
1#include <test/jtx.h>
2
3#include <xrpld/app/tx/detail/NFTokenUtils.h>
4
5#include <xrpl/basics/random.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/jss.h>
8
9#include <initializer_list>
10
11namespace ripple {
12
14{
15 FeatureBitset const disallowIncoming{featureDisallowIncoming};
16
17 // Helper function that returns the number of NFTs minted by an issuer.
18 static std::uint32_t
20 {
21 std::uint32_t ret{0};
22 if (auto const sleIssuer = env.le(issuer))
23 ret = sleIssuer->at(~sfMintedNFTokens).value_or(0);
24 return ret;
25 }
26
27 // Helper function that returns the number of an issuer's burned NFTs.
28 static std::uint32_t
30 {
31 std::uint32_t ret{0};
32 if (auto const sleIssuer = env.le(issuer))
33 ret = sleIssuer->at(~sfBurnedNFTokens).value_or(0);
34 return ret;
35 }
36
37 // Helper function that returns the number of nfts owned by an account.
38 static std::uint32_t
40 {
41 Json::Value params;
42 params[jss::account] = acct.human();
43 params[jss::type] = "state";
44 Json::Value nfts = env.rpc("json", "account_nfts", to_string(params));
45 return nfts[jss::result][jss::account_nfts].size();
46 };
47
48 // Helper function that returns the number of tickets held by an account.
49 static std::uint32_t
51 {
52 std::uint32_t ret{0};
53 if (auto const sleAcct = env.le(acct))
54 ret = sleAcct->at(~sfTicketCount).value_or(0);
55 return ret;
56 }
57
58 // Helper function returns the close time of the parent ledger.
61 {
62 return env.current()->info().parentCloseTime.time_since_epoch().count();
63 }
64
65 void
67 {
68 testcase("Enabled");
69
70 using namespace test::jtx;
71 {
72 // If the NFT amendment is enabled all NFT-related
73 // facilities should be available.
74 Env env{*this, features};
75 Account const& master = env.master;
76
77 BEAST_EXPECT(ownerCount(env, master) == 0);
78 BEAST_EXPECT(mintedCount(env, master) == 0);
79 BEAST_EXPECT(burnedCount(env, master) == 0);
80
81 uint256 const nftId0{token::getNextID(env, env.master, 0u)};
82 env(token::mint(env.master, 0u));
83 env.close();
84 BEAST_EXPECT(ownerCount(env, master) == 1);
85 BEAST_EXPECT(mintedCount(env, master) == 1);
86 BEAST_EXPECT(burnedCount(env, master) == 0);
87
88 env(token::burn(env.master, nftId0));
89 env.close();
90 BEAST_EXPECT(ownerCount(env, master) == 0);
91 BEAST_EXPECT(mintedCount(env, master) == 1);
92 BEAST_EXPECT(burnedCount(env, master) == 1);
93
94 uint256 const nftId1{
95 token::getNextID(env, env.master, 0u, tfTransferable)};
96 env(token::mint(env.master, 0u), txflags(tfTransferable));
97 env.close();
98 BEAST_EXPECT(ownerCount(env, master) == 1);
99 BEAST_EXPECT(mintedCount(env, master) == 2);
100 BEAST_EXPECT(burnedCount(env, master) == 1);
101
102 Account const alice{"alice"};
103 env.fund(XRP(10000), alice);
104 env.close();
105 uint256 const aliceOfferIndex =
106 keylet::nftoffer(alice, env.seq(alice)).key;
107 env(token::createOffer(alice, nftId1, XRP(1000)),
108 token::owner(master));
109 env.close();
110
111 BEAST_EXPECT(ownerCount(env, master) == 1);
112 BEAST_EXPECT(mintedCount(env, master) == 2);
113 BEAST_EXPECT(burnedCount(env, master) == 1);
114
115 BEAST_EXPECT(ownerCount(env, alice) == 1);
116 BEAST_EXPECT(mintedCount(env, alice) == 0);
117 BEAST_EXPECT(burnedCount(env, alice) == 0);
118
119 env(token::acceptBuyOffer(master, aliceOfferIndex));
120 env.close();
121
122 BEAST_EXPECT(ownerCount(env, master) == 0);
123 BEAST_EXPECT(mintedCount(env, master) == 2);
124 BEAST_EXPECT(burnedCount(env, master) == 1);
125
126 BEAST_EXPECT(ownerCount(env, alice) == 1);
127 BEAST_EXPECT(mintedCount(env, alice) == 0);
128 BEAST_EXPECT(burnedCount(env, alice) == 0);
129 }
130 }
131
132 void
134 {
135 // Verify that the reserve behaves as expected for minting.
136 testcase("Mint reserve");
137
138 using namespace test::jtx;
139
140 Env env{*this, features};
141 Account const alice{"alice"};
142 Account const minter{"minter"};
143
144 // Fund alice and minter enough to exist, but not enough to meet
145 // the reserve for creating their first NFT.
146 auto const acctReserve = env.current()->fees().reserve;
147 auto const incReserve = env.current()->fees().increment;
148 auto const baseFee = env.current()->fees().base;
149
150 env.fund(acctReserve, alice, minter);
151 env.close();
152
153 BEAST_EXPECT(env.balance(alice) == acctReserve);
154 BEAST_EXPECT(env.balance(minter) == acctReserve);
155 BEAST_EXPECT(ownerCount(env, alice) == 0);
156 BEAST_EXPECT(ownerCount(env, minter) == 0);
157
158 // alice does not have enough XRP to cover the reserve for an NFT
159 // page.
160 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
161 env.close();
162
163 BEAST_EXPECT(ownerCount(env, alice) == 0);
164 BEAST_EXPECT(mintedCount(env, alice) == 0);
165 BEAST_EXPECT(burnedCount(env, alice) == 0);
166
167 // Pay alice almost enough to make the reserve for an NFT page.
168 env(pay(env.master, alice, incReserve + drops(baseFee - 1)));
169 env.close();
170
171 // A lambda that checks alice's ownerCount, mintedCount, and
172 // burnedCount all in one fell swoop.
173 auto checkAliceOwnerMintedBurned = [&env, this, &alice](
174 std::uint32_t owners,
175 std::uint32_t minted,
176 std::uint32_t burned,
177 int line) {
178 auto oneCheck =
179 [line, this](
180 char const* type, std::uint32_t found, std::uint32_t exp) {
181 if (found == exp)
182 pass();
183 else
184 {
186 ss << "Wrong " << type << " count. Found: " << found
187 << "; Expected: " << exp;
188 fail(ss.str(), __FILE__, line);
189 }
190 };
191 oneCheck("owner", ownerCount(env, alice), owners);
192 oneCheck("minted", mintedCount(env, alice), minted);
193 oneCheck("burned", burnedCount(env, alice), burned);
194 };
195
196 // alice still does not have enough XRP for the reserve of an NFT
197 // page.
198 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
199 env.close();
200
201 checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
202
203 // Pay alice enough to make the reserve for an NFT page.
204 env(pay(env.master, alice, drops(baseFee + 1)));
205 env.close();
206
207 // Now alice can mint an NFT.
208 env(token::mint(alice));
209 env.close();
210
211 checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
212
213 // Alice should be able to mint an additional 31 NFTs without
214 // any additional reserve requirements.
215 for (int i = 1; i < 32; ++i)
216 {
217 env(token::mint(alice));
218 checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
219 }
220
221 // That NFT page is full. Creating an additional NFT page requires
222 // additional reserve.
223 env(token::mint(alice), ter(tecINSUFFICIENT_RESERVE));
224 env.close();
225 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
226
227 // Pay alice almost enough to make the reserve for an NFT page.
228 env(pay(env.master, alice, incReserve + drops(baseFee * 33 - 1)));
229 env.close();
230
231 // alice still does not have enough XRP for the reserve of an NFT
232 // page.
233 env(token::mint(alice), ter(tecINSUFFICIENT_RESERVE));
234 env.close();
235 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
236
237 // Pay alice enough to make the reserve for an NFT page.
238 env(pay(env.master, alice, drops(baseFee + 1)));
239 env.close();
240
241 // Now alice can mint an NFT.
242 env(token::mint(alice));
243 env.close();
244 checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
245
246 // alice burns the NFTs she created: check that pages consolidate
247 std::uint32_t seq = 0;
248
249 while (seq < 33)
250 {
251 env(token::burn(alice, token::getID(env, alice, 0, seq++)));
252 env.close();
253 checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
254 }
255
256 // alice burns a non-existent NFT.
257 env(token::burn(alice, token::getID(env, alice, 197, 5)),
258 ter(tecNO_ENTRY));
259 env.close();
260 checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
261
262 // That was fun! Now let's see what happens when we let someone
263 // else mint NFTs on alice's behalf. alice gives permission to
264 // minter.
265 env(token::setMinter(alice, minter));
266 env.close();
267 BEAST_EXPECT(
268 env.le(alice)->getAccountID(sfNFTokenMinter) == minter.id());
269
270 // A lambda that checks minter's and alice's ownerCount,
271 // mintedCount, and burnedCount all in one fell swoop.
272 auto checkMintersOwnerMintedBurned = [&env, this, &alice, &minter](
273 std::uint32_t aliceOwners,
274 std::uint32_t aliceMinted,
275 std::uint32_t aliceBurned,
276 std::uint32_t minterOwners,
277 std::uint32_t minterMinted,
278 std::uint32_t minterBurned,
279 int line) {
280 auto oneCheck = [this](
281 char const* type,
282 std::uint32_t found,
283 std::uint32_t exp,
284 int line) {
285 if (found == exp)
286 pass();
287 else
288 {
290 ss << "Wrong " << type << " count. Found: " << found
291 << "; Expected: " << exp;
292 fail(ss.str(), __FILE__, line);
293 }
294 };
295 oneCheck("alice owner", ownerCount(env, alice), aliceOwners, line);
296 oneCheck(
297 "alice minted", mintedCount(env, alice), aliceMinted, line);
298 oneCheck(
299 "alice burned", burnedCount(env, alice), aliceBurned, line);
300 oneCheck(
301 "minter owner", ownerCount(env, minter), minterOwners, line);
302 oneCheck(
303 "minter minted", mintedCount(env, minter), minterMinted, line);
304 oneCheck(
305 "minter burned", burnedCount(env, minter), minterBurned, line);
306 };
307
308 std::uint32_t nftSeq = 33;
309
310 // Pay minter almost enough to make the reserve for an NFT page.
311 env(pay(env.master, minter, incReserve - drops(1)));
312 env.close();
313 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
314
315 // minter still does not have enough XRP for the reserve of an NFT
316 // page. Just for grins (and code coverage), minter mints NFTs that
317 // include a URI.
318 env(token::mint(minter),
319 token::issuer(alice),
320 token::uri("uri"),
322 env.close();
323 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
324
325 // Pay minter enough to make the reserve for an NFT page.
326 env(pay(env.master, minter, drops(baseFee + 1)));
327 env.close();
328
329 // Now minter can mint an NFT for alice.
330 env(token::mint(minter), token::issuer(alice), token::uri("uri"));
331 env.close();
332 checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
333
334 // Minter should be able to mint an additional 31 NFTs for alice
335 // without any additional reserve requirements.
336 for (int i = 1; i < 32; ++i)
337 {
338 env(token::mint(minter), token::issuer(alice), token::uri("uri"));
339 checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
340 }
341
342 // Pay minter almost enough for the reserve of an additional NFT
343 // page.
344 env(pay(env.master, minter, incReserve + drops(baseFee * 32 - 1)));
345 env.close();
346
347 // That NFT page is full. Creating an additional NFT page requires
348 // additional reserve.
349 env(token::mint(minter),
350 token::issuer(alice),
351 token::uri("uri"),
353 env.close();
354 checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
355
356 // Pay minter enough for the reserve of an additional NFT page.
357 env(pay(env.master, minter, drops(baseFee + 1)));
358 env.close();
359
360 // Now minter can mint an NFT.
361 env(token::mint(minter), token::issuer(alice), token::uri("uri"));
362 env.close();
363 checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
364
365 // minter burns the NFTs she created.
366 while (nftSeq < 65)
367 {
368 env(token::burn(minter, token::getID(env, alice, 0, nftSeq++)));
369 env.close();
370 checkMintersOwnerMintedBurned(
371 0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
372 }
373
374 // minter has one more NFT to burn. Should take her owner count to
375 // 0.
376 env(token::burn(minter, token::getID(env, alice, 0, nftSeq++)));
377 env.close();
378 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
379
380 // minter burns a non-existent NFT.
381 env(token::burn(minter, token::getID(env, alice, 2009, 3)),
382 ter(tecNO_ENTRY));
383 env.close();
384 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
385 }
386
387 void
389 {
390 // Make sure that an account cannot cause the sfMintedNFTokens
391 // field to wrap by minting more than 0xFFFF'FFFF tokens.
392 testcase("Mint max tokens");
393
394 using namespace test::jtx;
395
396 Account const alice{"alice"};
397 Env env{*this, features};
398 env.fund(XRP(1000), alice);
399 env.close();
400
401 // We're going to hack the ledger in order to avoid generating
402 // 4 billion or so NFTs. Because we're hacking the ledger we
403 // need alice's account to have non-zero sfMintedNFTokens and
404 // sfBurnedNFTokens fields. This prevents an exception when the
405 // AccountRoot template is applied.
406 {
407 uint256 const nftId0{token::getNextID(env, alice, 0u)};
408 env(token::mint(alice, 0u));
409 env.close();
410
411 env(token::burn(alice, nftId0));
412 env.close();
413 }
414
415 // Note that we're bypassing almost all of the ledger's safety
416 // checks with this modify() call. If you call close() between
417 // here and the end of the test all the effort will be lost.
418 env.app().openLedger().modify(
419 [&alice](OpenView& view, beast::Journal j) {
420 // Get the account root we want to hijack.
421 auto const sle = view.read(keylet::account(alice.id()));
422 if (!sle)
423 return false; // This would be really surprising!
424
425 // Just for sanity's sake we'll check that the current value
426 // of sfMintedNFTokens matches what we expect.
427 auto replacement = std::make_shared<SLE>(*sle, sle->key());
428 if (replacement->getFieldU32(sfMintedNFTokens) != 1)
429 return false; // Unexpected test conditions.
430
431 // Wequence number is generated by sfFirstNFTokenSequence +
432 // sfMintedNFTokens. We can replace the two fields with any
433 // numbers as long as they add up to the largest valid number.
434 // In our case, sfFirstNFTokenSequence is set to the largest
435 // valid number, and sfMintedNFTokens is set to zero.
436 (*replacement)[sfFirstNFTokenSequence] = 0xFFFF'FFFE;
437 (*replacement)[sfMintedNFTokens] = 0x0000'0000;
438 view.rawReplace(replacement);
439 return true;
440 });
441
442 // See whether alice is at the boundary that causes an error.
443 env(token::mint(alice, 0u), ter(tesSUCCESS));
444 env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
445 }
446
447 void
449 {
450 // Explore many of the invalid ways to mint an NFT.
451 testcase("Mint invalid");
452
453 using namespace test::jtx;
454
455 Env env{*this, features};
456 Account const alice{"alice"};
457 Account const minter{"minter"};
458
459 // Fund alice and minter enough to exist, but not enough to meet
460 // the reserve for creating their first NFT. Account reserve for unit
461 // tests is 200 XRP, not 20.
462 env.fund(XRP(200), alice, minter);
463 env.close();
464
465 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
466 env.close();
467
468 // Fund alice enough to start minting NFTs.
469 env(pay(env.master, alice, XRP(1000)));
470 env.close();
471
472 //----------------------------------------------------------------------
473 // preflight
474
475 // Set a negative fee.
476 env(token::mint(alice, 0u),
477 fee(STAmount(10ull, true)),
478 ter(temBAD_FEE));
479
480 // Set an invalid flag.
481 env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
482
483 // Can't set a transfer fee if the NFT does not have the tfTRANSFERABLE
484 // flag set.
485 env(token::mint(alice, 0u),
486 token::xferFee(maxTransferFee),
487 ter(temMALFORMED));
488
489 // Set a bad transfer fee.
490 env(token::mint(alice, 0u),
491 token::xferFee(maxTransferFee + 1),
492 txflags(tfTransferable),
494
495 // Account can't also be issuer.
496 env(token::mint(alice, 0u), token::issuer(alice), ter(temMALFORMED));
497
498 // Invalid URI: zero length.
499 env(token::mint(alice, 0u), token::uri(""), ter(temMALFORMED));
500
501 // Invalid URI: too long.
502 env(token::mint(alice, 0u),
503 token::uri(std::string(maxTokenURILength + 1, 'q')),
504 ter(temMALFORMED));
505
506 //----------------------------------------------------------------------
507 // preclaim
508
509 // Non-existent issuer.
510 env(token::mint(alice, 0u),
511 token::issuer(Account("demon")),
512 ter(tecNO_ISSUER));
513
514 //----------------------------------------------------------------------
515 // doApply
516
517 // Existent issuer, but not given minting permission
518 env(token::mint(minter, 0u),
519 token::issuer(alice),
520 ter(tecNO_PERMISSION));
521 }
522
523 void
525 {
526 // Explore many of the invalid ways to burn an NFT.
527 testcase("Burn invalid");
528
529 using namespace test::jtx;
530
531 Env env{*this, features};
532 Account const alice{"alice"};
533 Account const buyer{"buyer"};
534 Account const minter{"minter"};
535 Account const gw("gw");
536 IOU const gwAUD(gw["AUD"]);
537
538 // Fund alice and minter enough to exist and create an NFT, but not
539 // enough to meet the reserve for creating their first NFTOffer.
540 // Account reserve for unit tests is 200 XRP, not 20.
541 env.fund(XRP(250), alice, buyer, minter, gw);
542 env.close();
543 BEAST_EXPECT(ownerCount(env, alice) == 0);
544
545 uint256 const nftAlice0ID =
546 token::getNextID(env, alice, 0, tfTransferable);
547 env(token::mint(alice, 0u), txflags(tfTransferable));
548 env.close();
549 BEAST_EXPECT(ownerCount(env, alice) == 1);
550
551 //----------------------------------------------------------------------
552 // preflight
553
554 // Set a negative fee.
555 env(token::burn(alice, nftAlice0ID),
556 fee(STAmount(10ull, true)),
557 ter(temBAD_FEE));
558 env.close();
559 BEAST_EXPECT(ownerCount(env, alice) == 1);
560
561 // Set an invalid flag.
562 env(token::burn(alice, nftAlice0ID),
563 txflags(0x00008000),
564 ter(temINVALID_FLAG));
565 env.close();
566 BEAST_EXPECT(ownerCount(env, buyer) == 0);
567
568 //----------------------------------------------------------------------
569 // preclaim
570
571 // Try to burn a token that doesn't exist.
572 env(token::burn(alice, token::getID(env, alice, 0, 1)),
573 ter(tecNO_ENTRY));
574 env.close();
575 BEAST_EXPECT(ownerCount(env, buyer) == 0);
576
577 // Can't burn a token with many buy or sell offers. But that is
578 // verified in testManyNftOffers().
579
580 //----------------------------------------------------------------------
581 // doApply
582 }
583
584 void
586 {
587 testcase("Invalid NFT offer create");
588
589 using namespace test::jtx;
590
591 Env env{*this, features};
592 Account const alice{"alice"};
593 Account const buyer{"buyer"};
594 Account const gw("gw");
595 IOU const gwAUD(gw["AUD"]);
596
597 // Fund alice enough to exist and create an NFT, but not
598 // enough to meet the reserve for creating their first NFTOffer.
599 // Account reserve for unit tests is 200 XRP, not 20.
600 env.fund(XRP(250), alice, buyer, gw);
601 env.close();
602 BEAST_EXPECT(ownerCount(env, alice) == 0);
603
604 uint256 const nftAlice0ID =
605 token::getNextID(env, alice, 0, tfTransferable, 10);
606 env(token::mint(alice, 0u),
607 txflags(tfTransferable),
608 token::xferFee(10));
609 env.close();
610 BEAST_EXPECT(ownerCount(env, alice) == 1);
611
612 uint256 const nftXrpOnlyID =
613 token::getNextID(env, alice, 0, tfOnlyXRP | tfTransferable);
614 env(token::mint(alice, 0), txflags(tfOnlyXRP | tfTransferable));
615 env.close();
616 BEAST_EXPECT(ownerCount(env, alice) == 1);
617
618 uint256 nftNoXferID = token::getNextID(env, alice, 0);
619 env(token::mint(alice, 0));
620 env.close();
621 BEAST_EXPECT(ownerCount(env, alice) == 1);
622
623 //----------------------------------------------------------------------
624 // preflight
625
626 // buyer burns a fee, so they no longer have enough XRP to cover the
627 // reserve for a token offer.
628 env(noop(buyer));
629 env.close();
630
631 // buyer tries to create an NFTokenOffer, but doesn't have the reserve.
632 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
633 token::owner(alice),
635 env.close();
636 BEAST_EXPECT(ownerCount(env, buyer) == 0);
637
638 // Set a negative fee.
639 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
640 fee(STAmount(10ull, true)),
641 ter(temBAD_FEE));
642 env.close();
643 BEAST_EXPECT(ownerCount(env, buyer) == 0);
644
645 // Set an invalid flag.
646 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
647 txflags(0x00008000),
648 ter(temINVALID_FLAG));
649 env.close();
650 BEAST_EXPECT(ownerCount(env, buyer) == 0);
651
652 // Set an invalid amount.
653 env(token::createOffer(buyer, nftXrpOnlyID, buyer["USD"](1)),
654 ter(temBAD_AMOUNT));
655 env(token::createOffer(buyer, nftAlice0ID, buyer["USD"](0)),
656 ter(temBAD_AMOUNT));
657 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
658 ter(temBAD_AMOUNT));
659 env.close();
660 BEAST_EXPECT(ownerCount(env, buyer) == 0);
661
662 // Set a bad expiration.
663 env(token::createOffer(buyer, nftAlice0ID, buyer["USD"](1)),
664 token::expiration(0),
665 ter(temBAD_EXPIRATION));
666 env.close();
667 BEAST_EXPECT(ownerCount(env, buyer) == 0);
668
669 // Invalid Owner field and tfSellToken flag relationships.
670 // A buy offer must specify the owner.
671 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
672 ter(temMALFORMED));
673 env.close();
674 BEAST_EXPECT(ownerCount(env, buyer) == 0);
675
676 // A sell offer must not specify the owner; the owner is implicit.
677 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
678 token::owner(alice),
679 txflags(tfSellNFToken),
680 ter(temMALFORMED));
681 env.close();
682 BEAST_EXPECT(ownerCount(env, alice) == 1);
683
684 // An owner may not offer to buy their own token.
685 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
686 token::owner(alice),
687 ter(temMALFORMED));
688 env.close();
689 BEAST_EXPECT(ownerCount(env, alice) == 1);
690
691 // The destination may not be the account submitting the transaction.
692 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
693 token::destination(alice),
694 txflags(tfSellNFToken),
695 ter(temMALFORMED));
696 env.close();
697 BEAST_EXPECT(ownerCount(env, alice) == 1);
698
699 // The destination must be an account already established in the ledger.
700 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
701 token::destination(Account("demon")),
702 txflags(tfSellNFToken),
703 ter(tecNO_DST));
704 env.close();
705 BEAST_EXPECT(ownerCount(env, alice) == 1);
706
707 //----------------------------------------------------------------------
708 // preclaim
709
710 // The new NFTokenOffer may not have passed its expiration time.
711 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
712 token::owner(alice),
713 token::expiration(lastClose(env)),
714 ter(tecEXPIRED));
715 env.close();
716 BEAST_EXPECT(ownerCount(env, buyer) == 0);
717
718 // The nftID must be present in the ledger.
719 env(token::createOffer(
720 buyer, token::getID(env, alice, 0, 1), XRP(1000)),
721 token::owner(alice),
722 ter(tecNO_ENTRY));
723 env.close();
724 BEAST_EXPECT(ownerCount(env, buyer) == 0);
725
726 // The nftID must be present in the ledger of a sell offer too.
727 env(token::createOffer(
728 alice, token::getID(env, alice, 0, 1), XRP(1000)),
729 txflags(tfSellNFToken),
730 ter(tecNO_ENTRY));
731 env.close();
732 BEAST_EXPECT(ownerCount(env, buyer) == 0);
733
734 // buyer must have the funds to pay for their offer.
735 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
736 token::owner(alice),
737 ter(tecNO_LINE));
738 env.close();
739 BEAST_EXPECT(ownerCount(env, buyer) == 0);
740
741 env(trust(buyer, gwAUD(1000)));
742 env.close();
743 BEAST_EXPECT(ownerCount(env, buyer) == 1);
744 env.close();
745
746 // Issuer (alice) must have a trust line for the offered funds.
747 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
748 token::owner(alice),
749 ter(tecNO_LINE));
750 env.close();
751 BEAST_EXPECT(ownerCount(env, buyer) == 1);
752
753 // Give alice the needed trust line, but freeze it.
754 env(trust(gw, alice["AUD"](999), tfSetFreeze));
755 env.close();
756
757 // Issuer (alice) must have a trust line for the offered funds and
758 // the trust line may not be frozen.
759 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
760 token::owner(alice),
761 ter(tecFROZEN));
762 env.close();
763 BEAST_EXPECT(ownerCount(env, buyer) == 1);
764
765 // Unfreeze alice's trustline.
766 env(trust(gw, alice["AUD"](999), tfClearFreeze));
767 env.close();
768
769 // Can't transfer the NFT if the transferable flag is not set.
770 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
771 token::owner(alice),
773 env.close();
774 BEAST_EXPECT(ownerCount(env, buyer) == 1);
775
776 // Give buyer the needed trust line, but freeze it.
777 env(trust(gw, buyer["AUD"](999), tfSetFreeze));
778 env.close();
779
780 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
781 token::owner(alice),
782 ter(tecFROZEN));
783 env.close();
784 BEAST_EXPECT(ownerCount(env, buyer) == 1);
785
786 // Unfreeze buyer's trust line, but buyer has no actual gwAUD.
787 // to cover the offer.
788 env(trust(gw, buyer["AUD"](999), tfClearFreeze));
789 env(trust(buyer, gwAUD(1000)));
790 env.close();
791
792 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
793 token::owner(alice),
794 ter(tecUNFUNDED_OFFER));
795 env.close();
796 BEAST_EXPECT(ownerCount(env, buyer) == 1); // the trust line.
797
798 //----------------------------------------------------------------------
799 // doApply
800
801 // Give buyer almost enough AUD to cover the offer...
802 env(pay(gw, buyer, gwAUD(999)));
803 env.close();
804
805 // However buyer doesn't have enough XRP to cover the reserve for
806 // an NFT offer.
807 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
808 token::owner(alice),
810 env.close();
811 BEAST_EXPECT(ownerCount(env, buyer) == 1);
812
813 // Give buyer almost enough XRP to cover the reserve.
814 auto const baseFee = env.current()->fees().base;
815 env(pay(env.master, buyer, XRP(50) + drops(baseFee * 12 - 1)));
816 env.close();
817
818 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
819 token::owner(alice),
821 env.close();
822 BEAST_EXPECT(ownerCount(env, buyer) == 1);
823
824 // Give buyer just enough XRP to cover the reserve for the offer.
825 env(pay(env.master, buyer, drops(baseFee + 1)));
826 env.close();
827
828 // We don't care whether the offer is fully funded until the offer is
829 // accepted. Success at last!
830 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
831 token::owner(alice),
832 ter(tesSUCCESS));
833 env.close();
834 BEAST_EXPECT(ownerCount(env, buyer) == 2);
835 }
836
837 void
839 {
840 testcase("Invalid NFT offer cancel");
841
842 using namespace test::jtx;
843
844 Env env{*this, features};
845 Account const alice{"alice"};
846 Account const buyer{"buyer"};
847 Account const gw("gw");
848 IOU const gwAUD(gw["AUD"]);
849
850 env.fund(XRP(1000), alice, buyer, gw);
851 env.close();
852 BEAST_EXPECT(ownerCount(env, alice) == 0);
853
854 uint256 const nftAlice0ID =
855 token::getNextID(env, alice, 0, tfTransferable);
856 env(token::mint(alice, 0u), txflags(tfTransferable));
857 env.close();
858 BEAST_EXPECT(ownerCount(env, alice) == 1);
859
860 // This is the offer we'll try to cancel.
861 uint256 const buyerOfferIndex =
862 keylet::nftoffer(buyer, env.seq(buyer)).key;
863 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
864 token::owner(alice),
865 ter(tesSUCCESS));
866 env.close();
867 BEAST_EXPECT(ownerCount(env, buyer) == 1);
868
869 //----------------------------------------------------------------------
870 // preflight
871
872 // Set a negative fee.
873 env(token::cancelOffer(buyer, {buyerOfferIndex}),
874 fee(STAmount(10ull, true)),
875 ter(temBAD_FEE));
876 env.close();
877 BEAST_EXPECT(ownerCount(env, buyer) == 1);
878
879 // Set an invalid flag.
880 env(token::cancelOffer(buyer, {buyerOfferIndex}),
881 txflags(0x00008000),
882 ter(temINVALID_FLAG));
883 env.close();
884 BEAST_EXPECT(ownerCount(env, buyer) == 1);
885
886 // Empty list of tokens to delete.
887 {
888 Json::Value jv = token::cancelOffer(buyer);
889 jv[sfNFTokenOffers.jsonName] = Json::arrayValue;
890 env(jv, ter(temMALFORMED));
891 env.close();
892 BEAST_EXPECT(ownerCount(env, buyer) == 1);
893 }
894
895 // List of tokens to delete is too long.
896 {
898 maxTokenOfferCancelCount + 1, buyerOfferIndex);
899
900 env(token::cancelOffer(buyer, offers), ter(temMALFORMED));
901 env.close();
902 BEAST_EXPECT(ownerCount(env, buyer) == 1);
903 }
904
905 // Duplicate entries are not allowed in the list of offers to cancel.
906 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
907 ter(temMALFORMED));
908 env.close();
909 BEAST_EXPECT(ownerCount(env, buyer) == 1);
910
911 // Provide neither offers to cancel nor a root index.
912 env(token::cancelOffer(buyer), ter(temMALFORMED));
913 env.close();
914 BEAST_EXPECT(ownerCount(env, buyer) == 1);
915
916 //----------------------------------------------------------------------
917 // preclaim
918
919 // Make a non-root directory that we can pass as a root index.
920 env(pay(env.master, gw, XRP(5000)));
921 env.close();
922 for (std::uint32_t i = 1; i < 34; ++i)
923 {
924 env(offer(gw, XRP(i), gwAUD(1)));
925 env.close();
926 }
927
928 {
929 // gw attempts to cancel a Check as through it is an NFTokenOffer.
930 auto const gwCheckId = keylet::check(gw, env.seq(gw)).key;
931 env(check::create(gw, env.master, XRP(300)));
932 env.close();
933
934 env(token::cancelOffer(gw, {gwCheckId}), ter(tecNO_PERMISSION));
935 env.close();
936
937 // Cancel the check so it doesn't mess up later tests.
938 env(check::cancel(gw, gwCheckId));
939 env.close();
940 }
941
942 // gw attempts to cancel an offer they don't have permission to cancel.
943 env(token::cancelOffer(gw, {buyerOfferIndex}), ter(tecNO_PERMISSION));
944 env.close();
945 BEAST_EXPECT(ownerCount(env, buyer) == 1);
946
947 //----------------------------------------------------------------------
948 // doApply
949 //
950 // The tefBAD_LEDGER conditions are too hard to test.
951 // But let's see a successful offer cancel.
952 env(token::cancelOffer(buyer, {buyerOfferIndex}));
953 env.close();
954 BEAST_EXPECT(ownerCount(env, buyer) == 0);
955 }
956
957 void
959 {
960 testcase("Invalid NFT offer accept");
961
962 using namespace test::jtx;
963
964 Env env{*this, features};
965 Account const alice{"alice"};
966 Account const buyer{"buyer"};
967 Account const gw("gw");
968 IOU const gwAUD(gw["AUD"]);
969
970 env.fund(XRP(1000), alice, buyer, gw);
971 env.close();
972 BEAST_EXPECT(ownerCount(env, alice) == 0);
973
974 uint256 const nftAlice0ID =
975 token::getNextID(env, alice, 0, tfTransferable);
976 env(token::mint(alice, 0u), txflags(tfTransferable));
977 env.close();
978 BEAST_EXPECT(ownerCount(env, alice) == 1);
979
980 uint256 const nftXrpOnlyID =
981 token::getNextID(env, alice, 0, tfOnlyXRP | tfTransferable);
982 env(token::mint(alice, 0), txflags(tfOnlyXRP | tfTransferable));
983 env.close();
984 BEAST_EXPECT(ownerCount(env, alice) == 1);
985
986 uint256 nftNoXferID = token::getNextID(env, alice, 0);
987 env(token::mint(alice, 0));
988 env.close();
989 BEAST_EXPECT(ownerCount(env, alice) == 1);
990
991 // alice creates sell offers for her nfts.
992 uint256 const plainOfferIndex =
993 keylet::nftoffer(alice, env.seq(alice)).key;
994 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
995 txflags(tfSellNFToken));
996 env.close();
997 BEAST_EXPECT(ownerCount(env, alice) == 2);
998
999 uint256 const audOfferIndex =
1000 keylet::nftoffer(alice, env.seq(alice)).key;
1001 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1002 txflags(tfSellNFToken));
1003 env.close();
1004 BEAST_EXPECT(ownerCount(env, alice) == 3);
1005
1006 uint256 const xrpOnlyOfferIndex =
1007 keylet::nftoffer(alice, env.seq(alice)).key;
1008 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1009 txflags(tfSellNFToken));
1010 env.close();
1011 BEAST_EXPECT(ownerCount(env, alice) == 4);
1012
1013 uint256 const noXferOfferIndex =
1014 keylet::nftoffer(alice, env.seq(alice)).key;
1015 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1016 txflags(tfSellNFToken));
1017 env.close();
1018 BEAST_EXPECT(ownerCount(env, alice) == 5);
1019
1020 // alice creates a sell offer that will expire soon.
1021 uint256 const aliceExpOfferIndex =
1022 keylet::nftoffer(alice, env.seq(alice)).key;
1023 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1024 txflags(tfSellNFToken),
1025 token::expiration(lastClose(env) + 5));
1026 env.close();
1027 BEAST_EXPECT(ownerCount(env, alice) == 6);
1028
1029 //----------------------------------------------------------------------
1030 // preflight
1031
1032 // Set a negative fee.
1033 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1034 fee(STAmount(10ull, true)),
1035 ter(temBAD_FEE));
1036 env.close();
1037 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1038
1039 // Set an invalid flag.
1040 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1041 txflags(0x00008000),
1042 ter(temINVALID_FLAG));
1043 env.close();
1044 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1045
1046 // Supply nether an sfNFTokenBuyOffer nor an sfNFTokenSellOffer field.
1047 {
1048 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1049 jv.removeMember(sfNFTokenSellOffer.jsonName);
1050 env(jv, ter(temMALFORMED));
1051 env.close();
1052 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1053 }
1054
1055 // A buy offer may not contain a sfNFTokenBrokerFee field.
1056 {
1057 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1058 jv[sfNFTokenBrokerFee.jsonName] =
1060 env(jv, ter(temMALFORMED));
1061 env.close();
1062 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1063 }
1064
1065 // A sell offer may not contain a sfNFTokenBrokerFee field.
1066 {
1067 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1068 jv[sfNFTokenBrokerFee.jsonName] =
1070 env(jv, ter(temMALFORMED));
1071 env.close();
1072 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1073 }
1074
1075 // A brokered offer may not contain a negative or zero brokerFee.
1076 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1077 token::brokerFee(gwAUD(0)),
1078 ter(temMALFORMED));
1079 env.close();
1080 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1081
1082 //----------------------------------------------------------------------
1083 // preclaim
1084
1085 // The buy offer must be non-zero.
1086 env(token::acceptBuyOffer(buyer, beast::zero),
1087 ter(tecOBJECT_NOT_FOUND));
1088 env.close();
1089 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1090
1091 // The buy offer must be present in the ledger.
1092 uint256 const missingOfferIndex = keylet::nftoffer(alice, 1).key;
1093 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1094 ter(tecOBJECT_NOT_FOUND));
1095 env.close();
1096 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1097
1098 // The buy offer must not have expired.
1099 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
1100 env.close();
1101 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1102
1103 // The sell offer must be non-zero.
1104 env(token::acceptSellOffer(buyer, beast::zero),
1105 ter(tecOBJECT_NOT_FOUND));
1106 env.close();
1107 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1108
1109 // The sell offer must be present in the ledger.
1110 env(token::acceptSellOffer(buyer, missingOfferIndex),
1111 ter(tecOBJECT_NOT_FOUND));
1112 env.close();
1113 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1114
1115 // The sell offer must not have expired.
1116 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
1117 env.close();
1118 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1119
1120 //----------------------------------------------------------------------
1121 // preclaim brokered
1122
1123 // alice and buyer need trustlines before buyer can to create an
1124 // offer for gwAUD.
1125 env(trust(alice, gwAUD(1000)));
1126 env(trust(buyer, gwAUD(1000)));
1127 env.close();
1128 env(pay(gw, buyer, gwAUD(30)));
1129 env.close();
1130 BEAST_EXPECT(ownerCount(env, alice) == 7);
1131 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1132
1133 // We're about to exercise offer brokering, so we need
1134 // corresponding buy and sell offers.
1135 {
1136 // buyer creates a buy offer for one of alice's nfts.
1137 uint256 const buyerOfferIndex =
1138 keylet::nftoffer(buyer, env.seq(buyer)).key;
1139 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1140 token::owner(alice));
1141 env.close();
1142 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1143
1144 // gw attempts to broker offers that are not for the same token.
1145 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1147 env.close();
1148 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1149
1150 // gw attempts to broker offers that are not for the same currency.
1151 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1153 env.close();
1154 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1155
1156 // In a brokered offer, the buyer must offer greater than or
1157 // equal to the selling price.
1158 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1160 env.close();
1161 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1162
1163 // Remove buyer's offer.
1164 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1165 env.close();
1166 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1167 }
1168 {
1169 // buyer creates a buy offer for one of alice's nfts.
1170 uint256 const buyerOfferIndex =
1171 keylet::nftoffer(buyer, env.seq(buyer)).key;
1172 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1173 token::owner(alice));
1174 env.close();
1175 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1176
1177 // Broker sets their fee in a denomination other than the one
1178 // used by the offers
1179 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1180 token::brokerFee(XRP(40)),
1182 env.close();
1183 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1184
1185 // Broker fee way too big.
1186 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1187 token::brokerFee(gwAUD(31)),
1189 env.close();
1190 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1191
1192 // Broker fee is smaller, but still too big once the offer
1193 // seller's minimum is taken into account.
1194 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1195 token::brokerFee(gwAUD(1.5)),
1197 env.close();
1198 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1199
1200 // Remove buyer's offer.
1201 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1202 env.close();
1203 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1204 }
1205 //----------------------------------------------------------------------
1206 // preclaim buy
1207 {
1208 // buyer creates a buy offer for one of alice's nfts.
1209 uint256 const buyerOfferIndex =
1210 keylet::nftoffer(buyer, env.seq(buyer)).key;
1211 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1212 token::owner(alice));
1213 env.close();
1214 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1215
1216 // Don't accept a buy offer if the sell flag is set.
1217 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1219 env.close();
1220 BEAST_EXPECT(ownerCount(env, alice) == 7);
1221
1222 // An account can't accept its own offer.
1223 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1225 env.close();
1226 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1227
1228 // An offer acceptor must have enough funds to pay for the offer.
1229 env(pay(buyer, gw, gwAUD(30)));
1230 env.close();
1231 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1232 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1234 env.close();
1235 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1236
1237 // alice gives her NFT to gw, so alice no longer owns nftAlice0.
1238 {
1239 uint256 const offerIndex =
1240 keylet::nftoffer(alice, env.seq(alice)).key;
1241 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1242 txflags(tfSellNFToken));
1243 env.close();
1244 env(token::acceptSellOffer(gw, offerIndex));
1245 env.close();
1246 BEAST_EXPECT(ownerCount(env, alice) == 7);
1247 }
1248 env(pay(gw, buyer, gwAUD(30)));
1249 env.close();
1250
1251 // alice can't accept a buy offer for an NFT she no longer owns.
1252 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1253 ter(tecNO_PERMISSION));
1254 env.close();
1255 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1256
1257 // Remove buyer's offer.
1258 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1259 env.close();
1260 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1261 }
1262 //----------------------------------------------------------------------
1263 // preclaim sell
1264 {
1265 // buyer creates a buy offer for one of alice's nfts.
1266 uint256 const buyerOfferIndex =
1267 keylet::nftoffer(buyer, env.seq(buyer)).key;
1268 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1269 token::owner(alice));
1270 env.close();
1271 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1272
1273 // Don't accept a sell offer without the sell flag set.
1274 env(token::acceptSellOffer(alice, buyerOfferIndex),
1276 env.close();
1277 BEAST_EXPECT(ownerCount(env, alice) == 7);
1278
1279 // An account can't accept its own offer.
1280 env(token::acceptSellOffer(alice, plainOfferIndex),
1282 env.close();
1283 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1284
1285 // The seller must currently be in possession of the token they
1286 // are selling. alice gave nftAlice0ID to gw.
1287 env(token::acceptSellOffer(buyer, plainOfferIndex),
1288 ter(tecNO_PERMISSION));
1289 env.close();
1290 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1291
1292 // gw gives nftAlice0ID back to alice. That allows us to check
1293 // buyer attempting to accept one of alice's offers with
1294 // insufficient funds.
1295 {
1296 uint256 const offerIndex =
1297 keylet::nftoffer(gw, env.seq(gw)).key;
1298 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1299 txflags(tfSellNFToken));
1300 env.close();
1301 env(token::acceptSellOffer(alice, offerIndex));
1302 env.close();
1303 BEAST_EXPECT(ownerCount(env, alice) == 7);
1304 }
1305 env(pay(buyer, gw, gwAUD(30)));
1306 env.close();
1307 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1308 env(token::acceptSellOffer(buyer, audOfferIndex),
1310 env.close();
1311 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1312 }
1313
1314 //----------------------------------------------------------------------
1315 // doApply
1316 //
1317 // As far as I can see none of the failure modes are accessible as
1318 // long as the preflight and preclaim conditions are met.
1319 }
1320
1321 void
1323 {
1324 // Exercise NFTs with flagBurnable set and not set.
1325 testcase("Mint flagBurnable");
1326
1327 using namespace test::jtx;
1328
1329 Env env{*this, features};
1330 Account const alice{"alice"};
1331 Account const buyer{"buyer"};
1332 Account const minter1{"minter1"};
1333 Account const minter2{"minter2"};
1334
1335 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1336 env.close();
1337 BEAST_EXPECT(ownerCount(env, alice) == 0);
1338
1339 // alice selects minter as her minter.
1340 env(token::setMinter(alice, minter1));
1341 env.close();
1342
1343 // A lambda that...
1344 // 1. creates an alice nft
1345 // 2. minted by minter and
1346 // 3. transfers that nft to buyer.
1347 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1348 std::uint32_t flags) {
1349 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1350 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1351 env.close();
1352
1353 uint256 const offerIndex =
1354 keylet::nftoffer(minter1, env.seq(minter1)).key;
1355 env(token::createOffer(minter1, nftID, XRP(0)),
1356 txflags(tfSellNFToken));
1357 env.close();
1358
1359 env(token::acceptSellOffer(buyer, offerIndex));
1360 env.close();
1361
1362 return nftID;
1363 };
1364
1365 // An NFT without flagBurnable can only be burned by its owner.
1366 {
1367 uint256 const noBurnID = nftToBuyer(0);
1368 env(token::burn(alice, noBurnID),
1369 token::owner(buyer),
1370 ter(tecNO_PERMISSION));
1371 env.close();
1372 env(token::burn(minter1, noBurnID),
1373 token::owner(buyer),
1374 ter(tecNO_PERMISSION));
1375 env.close();
1376 env(token::burn(minter2, noBurnID),
1377 token::owner(buyer),
1378 ter(tecNO_PERMISSION));
1379 env.close();
1380
1381 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1382 env(token::burn(buyer, noBurnID), token::owner(buyer));
1383 env.close();
1384 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1385 }
1386 // An NFT with flagBurnable can be burned by the issuer.
1387 {
1388 uint256 const burnableID = nftToBuyer(tfBurnable);
1389 env(token::burn(minter2, burnableID),
1390 token::owner(buyer),
1391 ter(tecNO_PERMISSION));
1392 env.close();
1393
1394 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1395 env(token::burn(alice, burnableID), token::owner(buyer));
1396 env.close();
1397 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1398 }
1399 // An NFT with flagBurnable can be burned by the owner.
1400 {
1401 uint256 const burnableID = nftToBuyer(tfBurnable);
1402 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1403 env(token::burn(buyer, burnableID));
1404 env.close();
1405 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1406 }
1407 // An NFT with flagBurnable can be burned by the minter.
1408 {
1409 uint256 const burnableID = nftToBuyer(tfBurnable);
1410 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1411 env(token::burn(buyer, burnableID), token::owner(buyer));
1412 env.close();
1413 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1414 }
1415 // An nft with flagBurnable may be burned by the issuers' minter,
1416 // who may not be the original minter.
1417 {
1418 uint256 const burnableID = nftToBuyer(tfBurnable);
1419 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1420
1421 env(token::setMinter(alice, minter2));
1422 env.close();
1423
1424 // minter1 is no longer alice's minter, so no longer has
1425 // permisson to burn alice's nfts.
1426 env(token::burn(minter1, burnableID),
1427 token::owner(buyer),
1428 ter(tecNO_PERMISSION));
1429 env.close();
1430 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1431
1432 // minter2, however, can burn alice's nfts.
1433 env(token::burn(minter2, burnableID), token::owner(buyer));
1434 env.close();
1435 BEAST_EXPECT(ownerCount(env, buyer) == 0);
1436 }
1437 }
1438
1439 void
1441 {
1442 // Exercise NFTs with flagOnlyXRP set and not set.
1443 testcase("Mint flagOnlyXRP");
1444
1445 using namespace test::jtx;
1446
1447 Env env{*this, features};
1448 Account const alice{"alice"};
1449 Account const buyer{"buyer"};
1450 Account const gw("gw");
1451 IOU const gwAUD(gw["AUD"]);
1452
1453 // Set trust lines so alice and buyer can use gwAUD.
1454 env.fund(XRP(1000), alice, buyer, gw);
1455 env.close();
1456 env(trust(alice, gwAUD(1000)));
1457 env(trust(buyer, gwAUD(1000)));
1458 env.close();
1459 env(pay(gw, buyer, gwAUD(100)));
1460
1461 // Don't set flagOnlyXRP and offers can be made with IOUs.
1462 {
1463 uint256 const nftIOUsOkayID{
1464 token::getNextID(env, alice, 0u, tfTransferable)};
1465 env(token::mint(alice, 0u), txflags(tfTransferable));
1466 env.close();
1467
1468 BEAST_EXPECT(ownerCount(env, alice) == 2);
1469 uint256 const aliceOfferIndex =
1470 keylet::nftoffer(alice, env.seq(alice)).key;
1471 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1472 txflags(tfSellNFToken));
1473 env.close();
1474 BEAST_EXPECT(ownerCount(env, alice) == 3);
1475
1476 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1477 uint256 const buyerOfferIndex =
1478 keylet::nftoffer(buyer, env.seq(buyer)).key;
1479 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1480 token::owner(alice));
1481 env.close();
1482 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1483
1484 // Cancel the two offers just to be tidy.
1485 env(token::cancelOffer(alice, {aliceOfferIndex}));
1486 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1487 env.close();
1488 BEAST_EXPECT(ownerCount(env, alice) == 2);
1489 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1490
1491 // Also burn alice's nft.
1492 env(token::burn(alice, nftIOUsOkayID));
1493 env.close();
1494 BEAST_EXPECT(ownerCount(env, alice) == 1);
1495 }
1496
1497 // Set flagOnlyXRP and offers using IOUs are rejected.
1498 {
1499 uint256 const nftOnlyXRPID{
1500 token::getNextID(env, alice, 0u, tfOnlyXRP | tfTransferable)};
1501 env(token::mint(alice, 0u), txflags(tfOnlyXRP | tfTransferable));
1502 env.close();
1503
1504 BEAST_EXPECT(ownerCount(env, alice) == 2);
1505 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1506 txflags(tfSellNFToken),
1507 ter(temBAD_AMOUNT));
1508 env.close();
1509 BEAST_EXPECT(ownerCount(env, alice) == 2);
1510
1511 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1512 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1513 token::owner(alice),
1514 ter(temBAD_AMOUNT));
1515 env.close();
1516 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1517
1518 // However offers for XRP are okay.
1519 BEAST_EXPECT(ownerCount(env, alice) == 2);
1520 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1521 txflags(tfSellNFToken));
1522 env.close();
1523 BEAST_EXPECT(ownerCount(env, alice) == 3);
1524
1525 BEAST_EXPECT(ownerCount(env, buyer) == 1);
1526 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1527 token::owner(alice));
1528 env.close();
1529 BEAST_EXPECT(ownerCount(env, buyer) == 2);
1530 }
1531 }
1532
1533 void
1535 {
1536 // Exercise NFTs with flagCreateTrustLines set and not set.
1537 testcase("Mint flagCreateTrustLines");
1538
1539 using namespace test::jtx;
1540
1541 Account const alice{"alice"};
1542 Account const becky{"becky"};
1543 Account const cheri{"cheri"};
1544 Account const gw("gw");
1545 IOU const gwAUD(gw["AUD"]);
1546 IOU const gwCAD(gw["CAD"]);
1547 IOU const gwEUR(gw["EUR"]);
1548
1549 // The behavior of this test changes dramatically based on the
1550 // presence (or absence) of the fixRemoveNFTokenAutoTrustLine
1551 // amendment. So we test both cases here.
1552 for (auto const& tweakedFeatures :
1553 {features - fixRemoveNFTokenAutoTrustLine,
1554 features | fixRemoveNFTokenAutoTrustLine})
1555 {
1556 Env env{*this, tweakedFeatures};
1557 env.fund(XRP(1000), alice, becky, cheri, gw);
1558 env.close();
1559
1560 // Set trust lines so becky and cheri can use gw's currency.
1561 env(trust(becky, gwAUD(1000)));
1562 env(trust(cheri, gwAUD(1000)));
1563 env(trust(becky, gwCAD(1000)));
1564 env(trust(cheri, gwCAD(1000)));
1565 env(trust(becky, gwEUR(1000)));
1566 env(trust(cheri, gwEUR(1000)));
1567 env.close();
1568 env(pay(gw, becky, gwAUD(500)));
1569 env(pay(gw, becky, gwCAD(500)));
1570 env(pay(gw, becky, gwEUR(500)));
1571 env(pay(gw, cheri, gwAUD(500)));
1572 env(pay(gw, cheri, gwCAD(500)));
1573 env.close();
1574
1575 // An nft without flagCreateTrustLines but with a non-zero transfer
1576 // fee will not allow creating offers that use IOUs for payment.
1577 for (std::uint32_t xferFee : {0, 1})
1578 {
1579 uint256 const nftNoAutoTrustID{
1580 token::getNextID(env, alice, 0u, tfTransferable, xferFee)};
1581 env(token::mint(alice, 0u),
1582 token::xferFee(xferFee),
1583 txflags(tfTransferable));
1584 env.close();
1585
1586 // becky buys the nft for 1 drop.
1587 uint256 const beckyBuyOfferIndex =
1588 keylet::nftoffer(becky, env.seq(becky)).key;
1589 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1590 token::owner(alice));
1591 env.close();
1592 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1593 env.close();
1594
1595 // becky attempts to sell the nft for AUD.
1596 TER const createOfferTER =
1597 xferFee ? TER(tecNO_LINE) : TER(tesSUCCESS);
1598 uint256 const beckyOfferIndex =
1599 keylet::nftoffer(becky, env.seq(becky)).key;
1600 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1601 txflags(tfSellNFToken),
1602 ter(createOfferTER));
1603 env.close();
1604
1605 // cheri offers to buy the nft for CAD.
1606 uint256 const cheriOfferIndex =
1607 keylet::nftoffer(cheri, env.seq(cheri)).key;
1608 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1609 token::owner(becky),
1610 ter(createOfferTER));
1611 env.close();
1612
1613 // To keep things tidy, cancel the offers.
1614 env(token::cancelOffer(becky, {beckyOfferIndex}));
1615 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1616 env.close();
1617 }
1618 // An nft with flagCreateTrustLines but with a non-zero transfer
1619 // fee allows transfers using IOUs for payment.
1620 {
1621 std::uint16_t transferFee = 10000; // 10%
1622
1623 uint256 const nftAutoTrustID{token::getNextID(
1624 env, alice, 0u, tfTransferable | tfTrustLine, transferFee)};
1625
1626 // If the fixRemoveNFTokenAutoTrustLine amendment is active
1627 // then this transaction fails.
1628 {
1629 TER const mintTER =
1630 tweakedFeatures[fixRemoveNFTokenAutoTrustLine]
1631 ? static_cast<TER>(temINVALID_FLAG)
1632 : static_cast<TER>(tesSUCCESS);
1633
1634 env(token::mint(alice, 0u),
1635 token::xferFee(transferFee),
1636 txflags(tfTransferable | tfTrustLine),
1637 ter(mintTER));
1638 env.close();
1639
1640 // If fixRemoveNFTokenAutoTrustLine is active the rest
1641 // of this test falls on its face.
1642 if (tweakedFeatures[fixRemoveNFTokenAutoTrustLine])
1643 break;
1644 }
1645 // becky buys the nft for 1 drop.
1646 uint256 const beckyBuyOfferIndex =
1647 keylet::nftoffer(becky, env.seq(becky)).key;
1648 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1649 token::owner(alice));
1650 env.close();
1651 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1652 env.close();
1653
1654 // becky sells the nft for AUD.
1655 uint256 const beckySellOfferIndex =
1656 keylet::nftoffer(becky, env.seq(becky)).key;
1657 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1658 txflags(tfSellNFToken));
1659 env.close();
1660 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1661 env.close();
1662
1663 // alice should now have a trust line for gwAUD.
1664 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1665
1666 // becky buys the nft back for CAD.
1667 uint256 const beckyBuyBackOfferIndex =
1668 keylet::nftoffer(becky, env.seq(becky)).key;
1669 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1670 token::owner(cheri));
1671 env.close();
1672 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1673 env.close();
1674
1675 // alice should now have a trust line for gwAUD and gwCAD.
1676 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1677 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1678 }
1679 // Now that alice has trust lines preestablished, an nft without
1680 // flagCreateTrustLines will work for preestablished trust lines.
1681 {
1682 std::uint16_t transferFee = 5000; // 5%
1683 uint256 const nftNoAutoTrustID{token::getNextID(
1684 env, alice, 0u, tfTransferable, transferFee)};
1685 env(token::mint(alice, 0u),
1686 token::xferFee(transferFee),
1687 txflags(tfTransferable));
1688 env.close();
1689
1690 // alice sells the nft using AUD.
1691 uint256 const aliceSellOfferIndex =
1692 keylet::nftoffer(alice, env.seq(alice)).key;
1693 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1694 txflags(tfSellNFToken));
1695 env.close();
1696 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1697 env.close();
1698
1699 // alice should now have AUD(210):
1700 // o 200 for this sale and
1701 // o 10 for the previous sale's fee.
1702 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1703
1704 // cheri can't sell the NFT for EUR, but can for CAD.
1705 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1706 txflags(tfSellNFToken),
1707 ter(tecNO_LINE));
1708 env.close();
1709 uint256 const cheriSellOfferIndex =
1710 keylet::nftoffer(cheri, env.seq(cheri)).key;
1711 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1712 txflags(tfSellNFToken));
1713 env.close();
1714 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1715 env.close();
1716
1717 // alice should now have CAD(10):
1718 // o 5 from this sale's fee and
1719 // o 5 for the previous sale's fee.
1720 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1721 }
1722 }
1723 }
1724
1725 void
1727 {
1728 // Exercise NFTs with flagTransferable set and not set.
1729 testcase("Mint flagTransferable");
1730
1731 using namespace test::jtx;
1732
1733 Env env{*this, features};
1734
1735 Account const alice{"alice"};
1736 Account const becky{"becky"};
1737 Account const minter{"minter"};
1738
1739 env.fund(XRP(1000), alice, becky, minter);
1740 env.close();
1741
1742 // First try an nft made by alice without flagTransferable set.
1743 {
1744 BEAST_EXPECT(ownerCount(env, alice) == 0);
1745 uint256 const nftAliceNoTransferID{
1746 token::getNextID(env, alice, 0u)};
1747 env(token::mint(alice, 0u), token::xferFee(0));
1748 env.close();
1749 BEAST_EXPECT(ownerCount(env, alice) == 1);
1750
1751 // becky tries to offer to buy alice's nft.
1752 BEAST_EXPECT(ownerCount(env, becky) == 0);
1753 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1754 token::owner(alice),
1756
1757 // alice offers to sell the nft and becky accepts the offer.
1758 uint256 const aliceSellOfferIndex =
1759 keylet::nftoffer(alice, env.seq(alice)).key;
1760 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1761 txflags(tfSellNFToken));
1762 env.close();
1763 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1764 env.close();
1765 BEAST_EXPECT(ownerCount(env, alice) == 0);
1766 BEAST_EXPECT(ownerCount(env, becky) == 1);
1767
1768 // becky tries to offer the nft for sale.
1769 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1770 txflags(tfSellNFToken),
1772 env.close();
1773 BEAST_EXPECT(ownerCount(env, alice) == 0);
1774 BEAST_EXPECT(ownerCount(env, becky) == 1);
1775
1776 // becky tries to offer the nft for sale with alice as the
1777 // destination. That also doesn't work.
1778 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1779 txflags(tfSellNFToken),
1780 token::destination(alice),
1782 env.close();
1783 BEAST_EXPECT(ownerCount(env, alice) == 0);
1784 BEAST_EXPECT(ownerCount(env, becky) == 1);
1785
1786 // alice offers to buy the nft back from becky. becky accepts
1787 // the offer.
1788 uint256 const aliceBuyOfferIndex =
1789 keylet::nftoffer(alice, env.seq(alice)).key;
1790 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1791 token::owner(becky));
1792 env.close();
1793 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1794 env.close();
1795 BEAST_EXPECT(ownerCount(env, alice) == 1);
1796 BEAST_EXPECT(ownerCount(env, becky) == 0);
1797
1798 // alice burns her nft so accounting is simpler below.
1799 env(token::burn(alice, nftAliceNoTransferID));
1800 env.close();
1801 BEAST_EXPECT(ownerCount(env, alice) == 0);
1802 BEAST_EXPECT(ownerCount(env, becky) == 0);
1803 }
1804 // Try an nft minted by minter for alice without flagTransferable set.
1805 {
1806 env(token::setMinter(alice, minter));
1807 env.close();
1808
1809 BEAST_EXPECT(ownerCount(env, minter) == 0);
1810 uint256 const nftMinterNoTransferID{
1811 token::getNextID(env, alice, 0u)};
1812 env(token::mint(minter), token::issuer(alice));
1813 env.close();
1814 BEAST_EXPECT(ownerCount(env, minter) == 1);
1815
1816 // becky tries to offer to buy minter's nft.
1817 BEAST_EXPECT(ownerCount(env, becky) == 0);
1818 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1819 token::owner(minter),
1821 env.close();
1822 BEAST_EXPECT(ownerCount(env, becky) == 0);
1823
1824 // alice removes authorization of minter.
1825 env(token::clearMinter(alice));
1826 env.close();
1827
1828 // minter tries to offer their nft for sale.
1829 BEAST_EXPECT(ownerCount(env, minter) == 1);
1830 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1831 txflags(tfSellNFToken),
1833 env.close();
1834 BEAST_EXPECT(ownerCount(env, minter) == 1);
1835
1836 // Let enough ledgers pass that old transactions are no longer
1837 // retried, then alice gives authorization back to minter.
1838 for (int i = 0; i < 10; ++i)
1839 env.close();
1840
1841 env(token::setMinter(alice, minter));
1842 env.close();
1843 BEAST_EXPECT(ownerCount(env, minter) == 1);
1844
1845 // minter successfully offers their nft for sale.
1846 BEAST_EXPECT(ownerCount(env, minter) == 1);
1847 uint256 const minterSellOfferIndex =
1848 keylet::nftoffer(minter, env.seq(minter)).key;
1849 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1850 txflags(tfSellNFToken));
1851 env.close();
1852 BEAST_EXPECT(ownerCount(env, minter) == 2);
1853
1854 // alice removes authorization of minter so we can see whether
1855 // minter's pre-existing offer still works.
1856 env(token::clearMinter(alice));
1857 env.close();
1858
1859 // becky buys minter's nft even though minter is no longer alice's
1860 // official minter.
1861 BEAST_EXPECT(ownerCount(env, becky) == 0);
1862 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1863 env.close();
1864 BEAST_EXPECT(ownerCount(env, becky) == 1);
1865 BEAST_EXPECT(ownerCount(env, minter) == 0);
1866
1867 // becky attempts to sell the nft.
1868 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1869 txflags(tfSellNFToken),
1871 env.close();
1872
1873 // Since minter is not, at the moment, alice's official minter
1874 // they cannot create an offer to buy the nft they minted.
1875 BEAST_EXPECT(ownerCount(env, minter) == 0);
1876 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1877 token::owner(becky),
1879 env.close();
1880 BEAST_EXPECT(ownerCount(env, minter) == 0);
1881
1882 // alice can create an offer to buy the nft.
1883 BEAST_EXPECT(ownerCount(env, alice) == 0);
1884 uint256 const aliceBuyOfferIndex =
1885 keylet::nftoffer(alice, env.seq(alice)).key;
1886 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1887 token::owner(becky));
1888 env.close();
1889 BEAST_EXPECT(ownerCount(env, alice) == 1);
1890
1891 // Let enough ledgers pass that old transactions are no longer
1892 // retried, then alice gives authorization back to minter.
1893 for (int i = 0; i < 10; ++i)
1894 env.close();
1895
1896 env(token::setMinter(alice, minter));
1897 env.close();
1898
1899 // Now minter can create an offer to buy the nft.
1900 BEAST_EXPECT(ownerCount(env, minter) == 0);
1901 uint256 const minterBuyOfferIndex =
1902 keylet::nftoffer(minter, env.seq(minter)).key;
1903 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1904 token::owner(becky));
1905 env.close();
1906 BEAST_EXPECT(ownerCount(env, minter) == 1);
1907
1908 // alice removes authorization of minter so we can see whether
1909 // minter's pre-existing buy offer still works.
1910 env(token::clearMinter(alice));
1911 env.close();
1912
1913 // becky accepts minter's sell offer.
1914 BEAST_EXPECT(ownerCount(env, minter) == 1);
1915 BEAST_EXPECT(ownerCount(env, becky) == 1);
1916 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1917 env.close();
1918 BEAST_EXPECT(ownerCount(env, minter) == 1);
1919 BEAST_EXPECT(ownerCount(env, becky) == 0);
1920 BEAST_EXPECT(ownerCount(env, alice) == 1);
1921
1922 // minter burns their nft and alice cancels her offer so the
1923 // next tests can start with a clean slate.
1924 env(token::burn(minter, nftMinterNoTransferID), ter(tesSUCCESS));
1925 env.close();
1926 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1927 env.close();
1928 BEAST_EXPECT(ownerCount(env, alice) == 0);
1929 BEAST_EXPECT(ownerCount(env, becky) == 0);
1930 BEAST_EXPECT(ownerCount(env, minter) == 0);
1931 }
1932 // nfts with flagTransferable set should be buyable and salable
1933 // by anybody.
1934 {
1935 BEAST_EXPECT(ownerCount(env, alice) == 0);
1936 uint256 const nftAliceID{
1937 token::getNextID(env, alice, 0u, tfTransferable)};
1938 env(token::mint(alice, 0u), txflags(tfTransferable));
1939 env.close();
1940 BEAST_EXPECT(ownerCount(env, alice) == 1);
1941
1942 // Both alice and becky can make offers for alice's nft.
1943 uint256 const aliceSellOfferIndex =
1944 keylet::nftoffer(alice, env.seq(alice)).key;
1945 env(token::createOffer(alice, nftAliceID, XRP(20)),
1946 txflags(tfSellNFToken));
1947 env.close();
1948 BEAST_EXPECT(ownerCount(env, alice) == 2);
1949
1950 uint256 const beckyBuyOfferIndex =
1951 keylet::nftoffer(becky, env.seq(becky)).key;
1952 env(token::createOffer(becky, nftAliceID, XRP(21)),
1953 token::owner(alice));
1954 env.close();
1955 BEAST_EXPECT(ownerCount(env, alice) == 2);
1956
1957 // becky accepts alice's sell offer.
1958 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1959 env.close();
1960 BEAST_EXPECT(ownerCount(env, alice) == 0);
1961 BEAST_EXPECT(ownerCount(env, becky) == 2);
1962
1963 // becky offers to sell the nft.
1964 uint256 const beckySellOfferIndex =
1965 keylet::nftoffer(becky, env.seq(becky)).key;
1966 env(token::createOffer(becky, nftAliceID, XRP(22)),
1967 txflags(tfSellNFToken));
1968 env.close();
1969 BEAST_EXPECT(ownerCount(env, alice) == 0);
1970 BEAST_EXPECT(ownerCount(env, becky) == 3);
1971
1972 // minter buys the nft (even though minter is not currently
1973 // alice's minter).
1974 env(token::acceptSellOffer(minter, beckySellOfferIndex));
1975 env.close();
1976 BEAST_EXPECT(ownerCount(env, alice) == 0);
1977 BEAST_EXPECT(ownerCount(env, becky) == 1);
1978 BEAST_EXPECT(ownerCount(env, minter) == 1);
1979
1980 // minter offers to sell the nft.
1981 uint256 const minterSellOfferIndex =
1982 keylet::nftoffer(minter, env.seq(minter)).key;
1983 env(token::createOffer(minter, nftAliceID, XRP(23)),
1984 txflags(tfSellNFToken));
1985 env.close();
1986 BEAST_EXPECT(ownerCount(env, alice) == 0);
1987 BEAST_EXPECT(ownerCount(env, becky) == 1);
1988 BEAST_EXPECT(ownerCount(env, minter) == 2);
1989
1990 // alice buys back the nft.
1991 env(token::acceptSellOffer(alice, minterSellOfferIndex));
1992 env.close();
1993 BEAST_EXPECT(ownerCount(env, alice) == 1);
1994 BEAST_EXPECT(ownerCount(env, becky) == 1);
1995 BEAST_EXPECT(ownerCount(env, minter) == 0);
1996
1997 // Remember the buy offer that becky made for alice's token way
1998 // back when? It's still in the ledger, and alice accepts it.
1999 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2000 env.close();
2001 BEAST_EXPECT(ownerCount(env, alice) == 0);
2002 BEAST_EXPECT(ownerCount(env, becky) == 1);
2003 BEAST_EXPECT(ownerCount(env, minter) == 0);
2004
2005 // Just for tidyness, becky burns the token before shutting
2006 // things down.
2007 env(token::burn(becky, nftAliceID));
2008 env.close();
2009 BEAST_EXPECT(ownerCount(env, alice) == 0);
2010 BEAST_EXPECT(ownerCount(env, becky) == 0);
2011 BEAST_EXPECT(ownerCount(env, minter) == 0);
2012 }
2013 }
2014
2015 void
2017 {
2018 // Exercise NFTs with and without a transferFee.
2019 testcase("Mint transferFee");
2020
2021 using namespace test::jtx;
2022
2023 Env env{*this, features};
2024 auto const baseFee = env.current()->fees().base;
2025
2026 Account const alice{"alice"};
2027 Account const becky{"becky"};
2028 Account const carol{"carol"};
2029 Account const minter{"minter"};
2030 Account const gw{"gw"};
2031 IOU const gwXAU(gw["XAU"]);
2032
2033 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2034 env.close();
2035
2036 env(trust(alice, gwXAU(2000)));
2037 env(trust(becky, gwXAU(2000)));
2038 env(trust(carol, gwXAU(2000)));
2039 env(trust(minter, gwXAU(2000)));
2040 env.close();
2041 env(pay(gw, alice, gwXAU(1000)));
2042 env(pay(gw, becky, gwXAU(1000)));
2043 env(pay(gw, carol, gwXAU(1000)));
2044 env(pay(gw, minter, gwXAU(1000)));
2045 env.close();
2046
2047 // Giving alice a minter helps us see if transfer rates are affected
2048 // by that.
2049 env(token::setMinter(alice, minter));
2050 env.close();
2051
2052 // If there is no transferFee, then alice gets nothing for the
2053 // transfer.
2054 {
2055 BEAST_EXPECT(ownerCount(env, alice) == 1);
2056 BEAST_EXPECT(ownerCount(env, becky) == 1);
2057 BEAST_EXPECT(ownerCount(env, carol) == 1);
2058 BEAST_EXPECT(ownerCount(env, minter) == 1);
2059
2060 uint256 const nftID =
2061 token::getNextID(env, alice, 0u, tfTransferable);
2062 env(token::mint(alice), txflags(tfTransferable));
2063 env.close();
2064
2065 // Becky buys the nft for XAU(10). Check balances.
2066 uint256 const beckyBuyOfferIndex =
2067 keylet::nftoffer(becky, env.seq(becky)).key;
2068 env(token::createOffer(becky, nftID, gwXAU(10)),
2069 token::owner(alice));
2070 env.close();
2071 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2072 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2073
2074 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2075 env.close();
2076 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2077 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2078
2079 // becky sells nft to carol. alice's balance should not change.
2080 uint256 const beckySellOfferIndex =
2081 keylet::nftoffer(becky, env.seq(becky)).key;
2082 env(token::createOffer(becky, nftID, gwXAU(10)),
2083 txflags(tfSellNFToken));
2084 env.close();
2085 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2086 env.close();
2087 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2088 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2089 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2090
2091 // minter buys nft from carol. alice's balance should not change.
2092 uint256 const minterBuyOfferIndex =
2093 keylet::nftoffer(minter, env.seq(minter)).key;
2094 env(token::createOffer(minter, nftID, gwXAU(10)),
2095 token::owner(carol));
2096 env.close();
2097 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2098 env.close();
2099 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2100 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2101 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2102 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2103
2104 // minter sells the nft to alice. gwXAU balances should finish
2105 // where they started.
2106 uint256 const minterSellOfferIndex =
2107 keylet::nftoffer(minter, env.seq(minter)).key;
2108 env(token::createOffer(minter, nftID, gwXAU(10)),
2109 txflags(tfSellNFToken));
2110 env.close();
2111 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2112 env.close();
2113 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2114 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2115 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2116 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2117
2118 // alice burns the nft to make later tests easier to think about.
2119 env(token::burn(alice, nftID));
2120 env.close();
2121 BEAST_EXPECT(ownerCount(env, alice) == 1);
2122 BEAST_EXPECT(ownerCount(env, becky) == 1);
2123 BEAST_EXPECT(ownerCount(env, carol) == 1);
2124 BEAST_EXPECT(ownerCount(env, minter) == 1);
2125 }
2126
2127 // Set the smallest possible transfer fee.
2128 {
2129 // An nft with a transfer fee of 1 basis point.
2130 uint256 const nftID =
2131 token::getNextID(env, alice, 0u, tfTransferable, 1);
2132 env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2133 env.close();
2134
2135 // Becky buys the nft for XAU(10). Check balances.
2136 uint256 const beckyBuyOfferIndex =
2137 keylet::nftoffer(becky, env.seq(becky)).key;
2138 env(token::createOffer(becky, nftID, gwXAU(10)),
2139 token::owner(alice));
2140 env.close();
2141 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2142 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2143
2144 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2145 env.close();
2146 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2147 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2148
2149 // becky sells nft to carol. alice's balance goes up.
2150 uint256 const beckySellOfferIndex =
2151 keylet::nftoffer(becky, env.seq(becky)).key;
2152 env(token::createOffer(becky, nftID, gwXAU(10)),
2153 txflags(tfSellNFToken));
2154 env.close();
2155 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2156 env.close();
2157
2158 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2159 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2160 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2161
2162 // minter buys nft from carol. alice's balance goes up.
2163 uint256 const minterBuyOfferIndex =
2164 keylet::nftoffer(minter, env.seq(minter)).key;
2165 env(token::createOffer(minter, nftID, gwXAU(10)),
2166 token::owner(carol));
2167 env.close();
2168 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2169 env.close();
2170
2171 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2172 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2173 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2174 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2175
2176 // minter sells the nft to alice. Because alice is part of the
2177 // transaction no transfer fee is removed.
2178 uint256 const minterSellOfferIndex =
2179 keylet::nftoffer(minter, env.seq(minter)).key;
2180 env(token::createOffer(minter, nftID, gwXAU(10)),
2181 txflags(tfSellNFToken));
2182 env.close();
2183 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2184 env.close();
2185 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2186 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2187 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2188 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2189
2190 // alice pays to becky and carol so subsequent tests are easier
2191 // to think about.
2192 env(pay(alice, becky, gwXAU(0.0001)));
2193 env(pay(alice, carol, gwXAU(0.0001)));
2194 env.close();
2195
2196 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2197 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2198 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2199 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2200
2201 // alice burns the nft to make later tests easier to think about.
2202 env(token::burn(alice, nftID));
2203 env.close();
2204 BEAST_EXPECT(ownerCount(env, alice) == 1);
2205 BEAST_EXPECT(ownerCount(env, becky) == 1);
2206 BEAST_EXPECT(ownerCount(env, carol) == 1);
2207 BEAST_EXPECT(ownerCount(env, minter) == 1);
2208 }
2209
2210 // Set the largest allowed transfer fee.
2211 {
2212 // A transfer fee greater than 50% is not allowed.
2213 env(token::mint(alice),
2214 txflags(tfTransferable),
2215 token::xferFee(maxTransferFee + 1),
2217 env.close();
2218
2219 // Make an nft with a transfer fee of 50%.
2220 uint256 const nftID = token::getNextID(
2221 env, alice, 0u, tfTransferable, maxTransferFee);
2222 env(token::mint(alice),
2223 txflags(tfTransferable),
2224 token::xferFee(maxTransferFee));
2225 env.close();
2226
2227 // Becky buys the nft for XAU(10). Check balances.
2228 uint256 const beckyBuyOfferIndex =
2229 keylet::nftoffer(becky, env.seq(becky)).key;
2230 env(token::createOffer(becky, nftID, gwXAU(10)),
2231 token::owner(alice));
2232 env.close();
2233 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2234 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2235
2236 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2237 env.close();
2238 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2239 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2240
2241 // becky sells nft to minter. alice's balance goes up.
2242 uint256 const beckySellOfferIndex =
2243 keylet::nftoffer(becky, env.seq(becky)).key;
2244 env(token::createOffer(becky, nftID, gwXAU(100)),
2245 txflags(tfSellNFToken));
2246 env.close();
2247 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2248 env.close();
2249
2250 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2251 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2252 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2253
2254 // carol buys nft from minter. alice's balance goes up.
2255 uint256 const carolBuyOfferIndex =
2256 keylet::nftoffer(carol, env.seq(carol)).key;
2257 env(token::createOffer(carol, nftID, gwXAU(10)),
2258 token::owner(minter));
2259 env.close();
2260 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2261 env.close();
2262
2263 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2264 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2265 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2266 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2267
2268 // carol sells the nft to alice. Because alice is part of the
2269 // transaction no transfer fee is removed.
2270 uint256 const carolSellOfferIndex =
2271 keylet::nftoffer(carol, env.seq(carol)).key;
2272 env(token::createOffer(carol, nftID, gwXAU(10)),
2273 txflags(tfSellNFToken));
2274 env.close();
2275 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2276 env.close();
2277
2278 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2279 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2280 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2281 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2282
2283 // rebalance so subsequent tests are easier to think about.
2284 env(pay(alice, minter, gwXAU(55)));
2285 env(pay(becky, minter, gwXAU(40)));
2286 env.close();
2287 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2288 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2289 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2290 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2291
2292 // alice burns the nft to make later tests easier to think about.
2293 env(token::burn(alice, nftID));
2294 env.close();
2295 BEAST_EXPECT(ownerCount(env, alice) == 1);
2296 BEAST_EXPECT(ownerCount(env, becky) == 1);
2297 BEAST_EXPECT(ownerCount(env, carol) == 1);
2298 BEAST_EXPECT(ownerCount(env, minter) == 1);
2299 }
2300
2301 // See the impact of rounding when the nft is sold for small amounts
2302 // of drops.
2303 for (auto NumberSwitchOver : {true})
2304 {
2305 if (NumberSwitchOver)
2306 env.enableFeature(fixUniversalNumber);
2307 else
2308 env.disableFeature(fixUniversalNumber);
2309
2310 // An nft with a transfer fee of 1 basis point.
2311 uint256 const nftID =
2312 token::getNextID(env, alice, 0u, tfTransferable, 1);
2313 env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2314 env.close();
2315
2316 // minter buys the nft for XRP(1). Since the transfer involves
2317 // alice there should be no transfer fee.
2318 STAmount aliceBalance = env.balance(alice);
2319 STAmount minterBalance = env.balance(minter);
2320 uint256 const minterBuyOfferIndex =
2321 keylet::nftoffer(minter, env.seq(minter)).key;
2322 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2323 env.close();
2324 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2325 env.close();
2326 aliceBalance += XRP(1) - baseFee;
2327 minterBalance -= XRP(1) + baseFee;
2328 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2329 BEAST_EXPECT(env.balance(minter) == minterBalance);
2330
2331 // minter sells to carol. The payment is just small enough that
2332 // alice does not get any transfer fee.
2333 auto pmt = NumberSwitchOver ? drops(50000) : drops(99999);
2334 STAmount carolBalance = env.balance(carol);
2335 uint256 const minterSellOfferIndex =
2336 keylet::nftoffer(minter, env.seq(minter)).key;
2337 env(token::createOffer(minter, nftID, pmt), txflags(tfSellNFToken));
2338 env.close();
2339 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2340 env.close();
2341 minterBalance += pmt - baseFee;
2342 carolBalance -= pmt + baseFee;
2343 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2344 BEAST_EXPECT(env.balance(minter) == minterBalance);
2345 BEAST_EXPECT(env.balance(carol) == carolBalance);
2346
2347 // carol sells to becky. This is the smallest amount to pay for a
2348 // transfer that enables a transfer fee of 1 basis point.
2349 STAmount beckyBalance = env.balance(becky);
2350 uint256 const beckyBuyOfferIndex =
2351 keylet::nftoffer(becky, env.seq(becky)).key;
2352 pmt = NumberSwitchOver ? drops(50001) : drops(100000);
2353 env(token::createOffer(becky, nftID, pmt), token::owner(carol));
2354 env.close();
2355 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2356 env.close();
2357 carolBalance += pmt - drops(1) - baseFee;
2358 beckyBalance -= pmt + baseFee;
2359 aliceBalance += drops(1);
2360
2361 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2362 BEAST_EXPECT(env.balance(minter) == minterBalance);
2363 BEAST_EXPECT(env.balance(carol) == carolBalance);
2364 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2365 }
2366
2367 // See the impact of rounding when the nft is sold for small amounts
2368 // of an IOU.
2369 {
2370 // An nft with a transfer fee of 1 basis point.
2371 uint256 const nftID =
2372 token::getNextID(env, alice, 0u, tfTransferable, 1);
2373 env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2374 env.close();
2375
2376 // Due to the floating point nature of IOUs we need to
2377 // significantly reduce the gwXAU balances of our accounts prior
2378 // to the iou transfer. Otherwise no transfers will happen.
2379 env(pay(alice, gw, env.balance(alice, gwXAU)));
2380 env(pay(minter, gw, env.balance(minter, gwXAU)));
2381 env(pay(becky, gw, env.balance(becky, gwXAU)));
2382 env.close();
2383
2384 STAmount const startXAUBalance(
2385 gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2386 env(pay(gw, alice, startXAUBalance));
2387 env(pay(gw, minter, startXAUBalance));
2388 env(pay(gw, becky, startXAUBalance));
2389 env.close();
2390
2391 // Here is the smallest expressible gwXAU amount.
2392 STAmount tinyXAU(
2394
2395 // minter buys the nft for tinyXAU. Since the transfer involves
2396 // alice there should be no transfer fee.
2397 STAmount aliceBalance = env.balance(alice, gwXAU);
2398 STAmount minterBalance = env.balance(minter, gwXAU);
2399 uint256 const minterBuyOfferIndex =
2400 keylet::nftoffer(minter, env.seq(minter)).key;
2401 env(token::createOffer(minter, nftID, tinyXAU),
2402 token::owner(alice));
2403 env.close();
2404 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2405 env.close();
2406 aliceBalance += tinyXAU;
2407 minterBalance -= tinyXAU;
2408 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2409 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2410
2411 // minter sells to carol.
2412 STAmount carolBalance = env.balance(carol, gwXAU);
2413 uint256 const minterSellOfferIndex =
2414 keylet::nftoffer(minter, env.seq(minter)).key;
2415 env(token::createOffer(minter, nftID, tinyXAU),
2416 txflags(tfSellNFToken));
2417 env.close();
2418 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2419 env.close();
2420
2421 minterBalance += tinyXAU;
2422 carolBalance -= tinyXAU;
2423 // tiny XAU is so small that alice does not get a transfer fee.
2424 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2425 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2426 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2427
2428 // carol sells to becky. This is the smallest gwXAU amount
2429 // to pay for a transfer that enables a transfer fee of 1.
2430 STAmount const cheapNFT(
2431 gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2432
2433 STAmount beckyBalance = env.balance(becky, gwXAU);
2434 uint256 const beckyBuyOfferIndex =
2435 keylet::nftoffer(becky, env.seq(becky)).key;
2436 env(token::createOffer(becky, nftID, cheapNFT),
2437 token::owner(carol));
2438 env.close();
2439 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2440 env.close();
2441
2442 aliceBalance += tinyXAU;
2443 beckyBalance -= cheapNFT;
2444 carolBalance += cheapNFT - tinyXAU;
2445 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2446 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2447 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2448 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2449 }
2450 }
2451
2452 void
2454 {
2455 // Exercise the NFT taxon field.
2456 testcase("Mint taxon");
2457
2458 using namespace test::jtx;
2459
2460 Env env{*this, features};
2461
2462 Account const alice{"alice"};
2463 Account const becky{"becky"};
2464
2465 env.fund(XRP(1000), alice, becky);
2466 env.close();
2467
2468 // The taxon field is incorporated straight into the NFT ID. So
2469 // tests only need to operate on NFT IDs; we don't need to generate
2470 // any transactions.
2471
2472 // The taxon value should be recoverable from the NFT ID.
2473 {
2474 uint256 const nftID = token::getNextID(env, alice, 0u);
2475 BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon(0));
2476 }
2477
2478 // Make sure the full range of taxon values work. We just tried
2479 // the minimum. Now try the largest.
2480 {
2481 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2482 BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon((0xFFFFFFFF)));
2483 }
2484
2485 // Do some touch testing to show that the taxon is recoverable no
2486 // matter what else changes around it in the nft ID.
2487 {
2488 std::uint32_t const taxon = rand_int<std::uint32_t>();
2489 for (int i = 0; i < 10; ++i)
2490 {
2491 // lambda to produce a useful message on error.
2492 auto check = [this](std::uint32_t taxon, uint256 const& nftID) {
2493 nft::Taxon const gotTaxon = nft::getTaxon(nftID);
2494 if (nft::toTaxon(taxon) == gotTaxon)
2495 pass();
2496 else
2497 {
2499 ss << "Taxon recovery failed from nftID "
2500 << to_string(nftID) << ". Expected: " << taxon
2501 << "; got: " << gotTaxon;
2502 fail(ss.str());
2503 }
2504 };
2505
2506 uint256 const nftAliceID = token::getID(
2507 env,
2508 alice,
2509 taxon,
2510 rand_int<std::uint32_t>(),
2511 rand_int<std::uint16_t>(),
2512 rand_int<std::uint16_t>());
2513 check(taxon, nftAliceID);
2514
2515 uint256 const nftBeckyID = token::getID(
2516 env,
2517 becky,
2518 taxon,
2519 rand_int<std::uint32_t>(),
2520 rand_int<std::uint16_t>(),
2521 rand_int<std::uint16_t>());
2522 check(taxon, nftBeckyID);
2523 }
2524 }
2525 }
2526
2527 void
2529 {
2530 // Exercise the NFT URI field.
2531 // 1. Create a number of NFTs with and without URIs.
2532 // 2. Retrieve the NFTs from the server.
2533 // 3. Make sure the right URI is attached to each NFT.
2534 testcase("Mint URI");
2535
2536 using namespace test::jtx;
2537
2538 Env env{*this, features};
2539
2540 Account const alice{"alice"};
2541 Account const becky{"becky"};
2542
2543 env.fund(XRP(10000), alice, becky);
2544 env.close();
2545
2546 // lambda that returns a randomly generated string which fits
2547 // the constraints of a URI. Empty strings may be returned.
2548 // In the empty string case do not add the URI to the nft.
2549 auto randURI = []() {
2550 std::string ret;
2551
2552 // About 20% of the returned strings should be empty
2553 if (rand_int(4) == 0)
2554 return ret;
2555
2556 std::size_t const strLen = rand_int(256);
2557 ret.reserve(strLen);
2558 for (std::size_t i = 0; i < strLen; ++i)
2559 ret.push_back(rand_byte());
2560
2561 return ret;
2562 };
2563
2564 // Make a list of URIs that we'll put in nfts.
2565 struct Entry
2566 {
2567 std::string uri;
2568 std::uint32_t taxon;
2569
2570 Entry(std::string uri_, std::uint32_t taxon_)
2571 : uri(std::move(uri_)), taxon(taxon_)
2572 {
2573 }
2574 };
2575
2576 std::vector<Entry> entries;
2577 entries.reserve(100);
2578 for (std::size_t i = 0; i < 100; ++i)
2579 entries.emplace_back(randURI(), rand_int<std::uint32_t>());
2580
2581 // alice creates nfts using entries.
2582 for (Entry const& entry : entries)
2583 {
2584 if (entry.uri.empty())
2585 {
2586 env(token::mint(alice, entry.taxon));
2587 }
2588 else
2589 {
2590 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2591 }
2592 env.close();
2593 }
2594
2595 // Recover alice's nfts from the ledger.
2596 Json::Value aliceNFTs = [&env, &alice]() {
2597 Json::Value params;
2598 params[jss::account] = alice.human();
2599 params[jss::type] = "state";
2600 return env.rpc("json", "account_nfts", to_string(params));
2601 }();
2602
2603 // Verify that the returned NFTs match what we sent.
2604 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2605 if (!BEAST_EXPECT(nfts.size() == entries.size()))
2606 return;
2607
2608 // Sort the returned NFTs by nft_serial so the are in the same order
2609 // as entries.
2610 std::vector<Json::Value> sortedNFTs;
2611 sortedNFTs.reserve(nfts.size());
2612 for (std::size_t i = 0; i < nfts.size(); ++i)
2613 sortedNFTs.push_back(nfts[i]);
2614 std::sort(
2615 sortedNFTs.begin(),
2616 sortedNFTs.end(),
2617 [](Json::Value const& lhs, Json::Value const& rhs) {
2618 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2619 });
2620
2621 for (std::size_t i = 0; i < entries.size(); ++i)
2622 {
2623 Entry const& entry = entries[i];
2624 Json::Value const& ret = sortedNFTs[i];
2625 BEAST_EXPECT(entry.taxon == ret[sfNFTokenTaxon.jsonName]);
2626 if (entry.uri.empty())
2627 {
2628 BEAST_EXPECT(!ret.isMember(sfURI.jsonName));
2629 }
2630 else
2631 {
2632 BEAST_EXPECT(strHex(entry.uri) == ret[sfURI.jsonName]);
2633 }
2634 }
2635 }
2636
2637 void
2639 {
2640 // Explore the CreateOffer Destination field.
2641 testcase("Create offer destination");
2642
2643 using namespace test::jtx;
2644
2645 Env env{*this, features};
2646
2647 Account const issuer{"issuer"};
2648 Account const minter{"minter"};
2649 Account const buyer{"buyer"};
2650 Account const broker{"broker"};
2651
2652 env.fund(XRP(1000), issuer, minter, buyer, broker);
2653
2654 // We want to explore how issuers vs minters fits into the permission
2655 // scheme. So issuer issues and minter mints.
2656 env(token::setMinter(issuer, minter));
2657 env.close();
2658
2659 uint256 const nftokenID =
2660 token::getNextID(env, issuer, 0, tfTransferable);
2661 env(token::mint(minter, 0),
2662 token::issuer(issuer),
2663 txflags(tfTransferable));
2664 env.close();
2665
2666 // Test how adding a Destination field to an offer affects permissions
2667 // for canceling offers.
2668 {
2669 uint256 const offerMinterToIssuer =
2670 keylet::nftoffer(minter, env.seq(minter)).key;
2671 env(token::createOffer(minter, nftokenID, drops(1)),
2672 token::destination(issuer),
2673 txflags(tfSellNFToken));
2674
2675 uint256 const offerMinterToBuyer =
2676 keylet::nftoffer(minter, env.seq(minter)).key;
2677 env(token::createOffer(minter, nftokenID, drops(1)),
2678 token::destination(buyer),
2679 txflags(tfSellNFToken));
2680
2681 uint256 const offerIssuerToMinter =
2682 keylet::nftoffer(issuer, env.seq(issuer)).key;
2683 env(token::createOffer(issuer, nftokenID, drops(1)),
2684 token::owner(minter),
2685 token::destination(minter));
2686
2687 uint256 const offerIssuerToBuyer =
2688 keylet::nftoffer(issuer, env.seq(issuer)).key;
2689 env(token::createOffer(issuer, nftokenID, drops(1)),
2690 token::owner(minter),
2691 token::destination(buyer));
2692
2693 env.close();
2694 BEAST_EXPECT(ownerCount(env, issuer) == 2);
2695 BEAST_EXPECT(ownerCount(env, minter) == 3);
2696 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2697
2698 // Test who gets to cancel the offers. Anyone outside of the
2699 // offer-owner/destination pair should not be able to cancel the
2700 // offers.
2701 //
2702 // Note that issuer does not have any special permissions regarding
2703 // offer cancellation. issuer cannot cancel an offer for an
2704 // NFToken they issued.
2705 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2706 ter(tecNO_PERMISSION));
2707 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2708 ter(tecNO_PERMISSION));
2709 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2710 ter(tecNO_PERMISSION));
2711 env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2712 ter(tecNO_PERMISSION));
2713 env.close();
2714 BEAST_EXPECT(ownerCount(env, issuer) == 2);
2715 BEAST_EXPECT(ownerCount(env, minter) == 3);
2716 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2717
2718 // Both the offer creator and and destination should be able to
2719 // cancel the offers.
2720 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2721 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2722 env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2723 env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2724 env.close();
2725 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2726 BEAST_EXPECT(ownerCount(env, minter) == 1);
2727 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2728 }
2729
2730 // Test how adding a Destination field to a sell offer affects
2731 // accepting that offer.
2732 {
2733 uint256 const offerMinterSellsToBuyer =
2734 keylet::nftoffer(minter, env.seq(minter)).key;
2735 env(token::createOffer(minter, nftokenID, drops(1)),
2736 token::destination(buyer),
2737 txflags(tfSellNFToken));
2738 env.close();
2739 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2740 BEAST_EXPECT(ownerCount(env, minter) == 2);
2741 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2742
2743 // issuer cannot accept a sell offer where they are not the
2744 // destination.
2745 env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2746 ter(tecNO_PERMISSION));
2747 env.close();
2748 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2749 BEAST_EXPECT(ownerCount(env, minter) == 2);
2750 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2751
2752 // However buyer can accept the sell offer.
2753 env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2754 env.close();
2755 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2756 BEAST_EXPECT(ownerCount(env, minter) == 0);
2757 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2758 }
2759
2760 // Test how adding a Destination field to a buy offer affects
2761 // accepting that offer.
2762 {
2763 uint256 const offerMinterBuysFromBuyer =
2764 keylet::nftoffer(minter, env.seq(minter)).key;
2765 env(token::createOffer(minter, nftokenID, drops(1)),
2766 token::owner(buyer),
2767 token::destination(buyer));
2768 env.close();
2769 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2770 BEAST_EXPECT(ownerCount(env, minter) == 1);
2771 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2772
2773 // issuer cannot accept a buy offer where they are the
2774 // destination.
2775 env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2776 ter(tecNO_PERMISSION));
2777 env.close();
2778 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2779 BEAST_EXPECT(ownerCount(env, minter) == 1);
2780 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2781
2782 // Buyer accepts minter's offer.
2783 env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2784 env.close();
2785 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2786 BEAST_EXPECT(ownerCount(env, minter) == 1);
2787 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2788
2789 // If a destination other than the NFToken owner is set, that
2790 // destination must act as a broker. The NFToken owner may not
2791 // simply accept the offer.
2792 uint256 const offerBuyerBuysFromMinter =
2793 keylet::nftoffer(buyer, env.seq(buyer)).key;
2794 env(token::createOffer(buyer, nftokenID, drops(1)),
2795 token::owner(minter),
2796 token::destination(broker));
2797 env.close();
2798 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2799 BEAST_EXPECT(ownerCount(env, minter) == 1);
2800 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2801
2802 env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2803 ter(tecNO_PERMISSION));
2804 env.close();
2805
2806 // Clean up the unused offer.
2807 env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2808 env.close();
2809 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2810 BEAST_EXPECT(ownerCount(env, minter) == 1);
2811 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2812 }
2813
2814 // Show that a sell offer's Destination can broker that sell offer
2815 // to another account.
2816 {
2817 uint256 const offerMinterToBroker =
2818 keylet::nftoffer(minter, env.seq(minter)).key;
2819 env(token::createOffer(minter, nftokenID, drops(1)),
2820 token::destination(broker),
2821 txflags(tfSellNFToken));
2822
2823 uint256 const offerBuyerToMinter =
2824 keylet::nftoffer(buyer, env.seq(buyer)).key;
2825 env(token::createOffer(buyer, nftokenID, drops(1)),
2826 token::owner(minter));
2827
2828 env.close();
2829 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2830 BEAST_EXPECT(ownerCount(env, minter) == 2);
2831 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2832
2833 {
2834 // issuer cannot broker the offers, because they are not the
2835 // Destination.
2836 env(token::brokerOffers(
2837 issuer, offerBuyerToMinter, offerMinterToBroker),
2838 ter(tecNO_PERMISSION));
2839 env.close();
2840 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2841 BEAST_EXPECT(ownerCount(env, minter) == 2);
2842 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2843 }
2844
2845 // Since broker is the sell offer's destination, they can broker
2846 // the two offers.
2847 env(token::brokerOffers(
2848 broker, offerBuyerToMinter, offerMinterToBroker));
2849 env.close();
2850 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2851 BEAST_EXPECT(ownerCount(env, minter) == 0);
2852 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2853 }
2854
2855 // Show that brokered mode cannot complete a transfer where the
2856 // Destination doesn't match, but can complete if the Destination
2857 // does match.
2858 {
2859 uint256 const offerBuyerToMinter =
2860 keylet::nftoffer(buyer, env.seq(buyer)).key;
2861 env(token::createOffer(buyer, nftokenID, drops(1)),
2862 token::destination(minter),
2863 txflags(tfSellNFToken));
2864
2865 uint256 const offerMinterToBuyer =
2866 keylet::nftoffer(minter, env.seq(minter)).key;
2867 env(token::createOffer(minter, nftokenID, drops(1)),
2868 token::owner(buyer));
2869
2870 uint256 const offerIssuerToBuyer =
2871 keylet::nftoffer(issuer, env.seq(issuer)).key;
2872 env(token::createOffer(issuer, nftokenID, drops(1)),
2873 token::owner(buyer));
2874
2875 env.close();
2876 BEAST_EXPECT(ownerCount(env, issuer) == 1);
2877 BEAST_EXPECT(ownerCount(env, minter) == 1);
2878 BEAST_EXPECT(ownerCount(env, buyer) == 2);
2879
2880 {
2881 // Cannot broker offers when the sell destination is not the
2882 // buyer.
2883 env(token::brokerOffers(
2884 broker, offerIssuerToBuyer, offerBuyerToMinter),
2885 ter(tecNO_PERMISSION));
2886 env.close();
2887
2888 BEAST_EXPECT(ownerCount(env, issuer) == 1);
2889 BEAST_EXPECT(ownerCount(env, minter) == 1);
2890 BEAST_EXPECT(ownerCount(env, buyer) == 2);
2891
2892 env(token::brokerOffers(
2893 broker, offerMinterToBuyer, offerBuyerToMinter),
2894 ter(tecNO_PERMISSION));
2895 env.close();
2896
2897 // Buyer is successful with acceptOffer.
2898 env(token::acceptBuyOffer(buyer, offerMinterToBuyer));
2899 env.close();
2900
2901 // Clean out the unconsumed offer.
2902 env(token::cancelOffer(buyer, {offerBuyerToMinter}));
2903 env.close();
2904
2905 BEAST_EXPECT(ownerCount(env, issuer) == 1);
2906 BEAST_EXPECT(ownerCount(env, minter) == 1);
2907 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2908
2909 // Clean out the unconsumed offer.
2910 env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
2911 env.close();
2912 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2913 BEAST_EXPECT(ownerCount(env, minter) == 1);
2914 BEAST_EXPECT(ownerCount(env, buyer) == 0);
2915 return;
2916 }
2917 }
2918
2919 // Show that if a buy and a sell offer both have the same destination,
2920 // then that destination can broker the offers.
2921 {
2922 uint256 const offerMinterToBroker =
2923 keylet::nftoffer(minter, env.seq(minter)).key;
2924 env(token::createOffer(minter, nftokenID, drops(1)),
2925 token::destination(broker),
2926 txflags(tfSellNFToken));
2927
2928 uint256 const offerBuyerToBroker =
2929 keylet::nftoffer(buyer, env.seq(buyer)).key;
2930 env(token::createOffer(buyer, nftokenID, drops(1)),
2931 token::owner(minter),
2932 token::destination(broker));
2933
2934 {
2935 // Cannot broker offers when the sell destination is not the
2936 // buyer or the broker.
2937 env(token::brokerOffers(
2938 issuer, offerBuyerToBroker, offerMinterToBroker),
2939 ter(tecNO_PERMISSION));
2940 env.close();
2941 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2942 BEAST_EXPECT(ownerCount(env, minter) == 2);
2943 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2944 }
2945
2946 // Broker is successful if they are the destination of both offers.
2947 env(token::brokerOffers(
2948 broker, offerBuyerToBroker, offerMinterToBroker));
2949 env.close();
2950 BEAST_EXPECT(ownerCount(env, issuer) == 0);
2951 BEAST_EXPECT(ownerCount(env, minter) == 0);
2952 BEAST_EXPECT(ownerCount(env, buyer) == 1);
2953 }
2954 }
2955
2956 void
2958 {
2959 testcase("Create offer destination disallow incoming");
2960
2961 using namespace test::jtx;
2962
2963 // test flag doesn't set unless amendment enabled
2964 {
2965 Env env{*this, features - disallowIncoming};
2966 Account const alice{"alice"};
2967 env.fund(XRP(10000), alice);
2968 env(fset(alice, asfDisallowIncomingNFTokenOffer));
2969 env.close();
2970 auto const sle = env.le(alice);
2971 uint32_t flags = sle->getFlags();
2972 BEAST_EXPECT(!(flags & lsfDisallowIncomingNFTokenOffer));
2973 }
2974
2975 Env env{*this, features | disallowIncoming};
2976
2977 Account const issuer{"issuer"};
2978 Account const minter{"minter"};
2979 Account const buyer{"buyer"};
2980 Account const alice{"alice"};
2981
2982 env.fund(XRP(1000), issuer, minter, buyer, alice);
2983
2984 env(token::setMinter(issuer, minter));
2985 env.close();
2986
2987 uint256 const nftokenID =
2988 token::getNextID(env, issuer, 0, tfTransferable);
2989 env(token::mint(minter, 0),
2990 token::issuer(issuer),
2991 txflags(tfTransferable));
2992 env.close();
2993
2994 // enable flag
2995 env(fset(buyer, asfDisallowIncomingNFTokenOffer));
2996 env.close();
2997
2998 // a sell offer from the minter to the buyer should be rejected
2999 {
3000 env(token::createOffer(minter, nftokenID, drops(1)),
3001 token::destination(buyer),
3002 txflags(tfSellNFToken),
3003 ter(tecNO_PERMISSION));
3004 env.close();
3005 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3006 BEAST_EXPECT(ownerCount(env, minter) == 1);
3007 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3008 }
3009
3010 // disable the flag
3011 env(fclear(buyer, asfDisallowIncomingNFTokenOffer));
3012 env.close();
3013
3014 // create offer (allowed now) then cancel
3015 {
3016 uint256 const offerIndex =
3017 keylet::nftoffer(minter, env.seq(minter)).key;
3018
3019 env(token::createOffer(minter, nftokenID, drops(1)),
3020 token::destination(buyer),
3021 txflags(tfSellNFToken));
3022 env.close();
3023
3024 env(token::cancelOffer(minter, {offerIndex}));
3025 env.close();
3026 }
3027
3028 // create offer, enable flag, then cancel
3029 {
3030 uint256 const offerIndex =
3031 keylet::nftoffer(minter, env.seq(minter)).key;
3032
3033 env(token::createOffer(minter, nftokenID, drops(1)),
3034 token::destination(buyer),
3035 txflags(tfSellNFToken));
3036 env.close();
3037
3038 env(fset(buyer, asfDisallowIncomingNFTokenOffer));
3039 env.close();
3040
3041 env(token::cancelOffer(minter, {offerIndex}));
3042 env.close();
3043
3044 env(fclear(buyer, asfDisallowIncomingNFTokenOffer));
3045 env.close();
3046 }
3047
3048 // create offer then transfer
3049 {
3050 uint256 const offerIndex =
3051 keylet::nftoffer(minter, env.seq(minter)).key;
3052
3053 env(token::createOffer(minter, nftokenID, drops(1)),
3054 token::destination(buyer),
3055 txflags(tfSellNFToken));
3056 env.close();
3057
3058 env(token::acceptSellOffer(buyer, offerIndex));
3059 env.close();
3060 }
3061
3062 // buyer now owns the token
3063
3064 // enable flag again
3065 env(fset(buyer, asfDisallowIncomingNFTokenOffer));
3066 env.close();
3067
3068 // a random offer to buy the token
3069 {
3070 env(token::createOffer(alice, nftokenID, drops(1)),
3071 token::owner(buyer),
3072 ter(tecNO_PERMISSION));
3073 env.close();
3074 }
3075
3076 // minter offer to buy the token
3077 {
3078 env(token::createOffer(minter, nftokenID, drops(1)),
3079 token::owner(buyer),
3080 ter(tecNO_PERMISSION));
3081 env.close();
3082 }
3083
3084 // minter mint and offer to buyer
3085 if (features[featureNFTokenMintOffer])
3086 {
3087 // enable flag
3088 env(fset(buyer, asfDisallowIncomingNFTokenOffer));
3089 // a sell offer from the minter to the buyer should be rejected
3090 env(token::mint(minter),
3091 token::amount(drops(1)),
3092 token::destination(buyer),
3093 ter(tecNO_PERMISSION));
3094 env.close();
3095
3096 // disable flag
3097 env(fclear(buyer, asfDisallowIncomingNFTokenOffer));
3098 env(token::mint(minter),
3099 token::amount(drops(1)),
3100 token::destination(buyer));
3101 env.close();
3102 }
3103 }
3104
3105 void
3107 {
3108 // Explore the CreateOffer Expiration field.
3109 testcase("Create offer expiration");
3110
3111 using namespace test::jtx;
3112
3113 Env env{*this, features};
3114
3115 Account const issuer{"issuer"};
3116 Account const minter{"minter"};
3117 Account const buyer{"buyer"};
3118
3119 env.fund(XRP(1000), issuer, minter, buyer);
3120
3121 // We want to explore how issuers vs minters fits into the permission
3122 // scheme. So issuer issues and minter mints.
3123 env(token::setMinter(issuer, minter));
3124 env.close();
3125
3126 uint256 const nftokenID0 =
3127 token::getNextID(env, issuer, 0, tfTransferable);
3128 env(token::mint(minter, 0),
3129 token::issuer(issuer),
3130 txflags(tfTransferable));
3131 env.close();
3132
3133 uint256 const nftokenID1 =
3134 token::getNextID(env, issuer, 0, tfTransferable);
3135 env(token::mint(minter, 0),
3136 token::issuer(issuer),
3137 txflags(tfTransferable));
3138 env.close();
3139
3140 // Test how adding an Expiration field to an offer affects permissions
3141 // for cancelling offers.
3142 {
3143 std::uint32_t const expiration = lastClose(env) + 25;
3144
3145 uint256 const offerMinterToIssuer =
3146 keylet::nftoffer(minter, env.seq(minter)).key;
3147 env(token::createOffer(minter, nftokenID0, drops(1)),
3148 token::destination(issuer),
3149 token::expiration(expiration),
3150 txflags(tfSellNFToken));
3151
3152 uint256 const offerMinterToAnyone =
3153 keylet::nftoffer(minter, env.seq(minter)).key;
3154 env(token::createOffer(minter, nftokenID0, drops(1)),
3155 token::expiration(expiration),
3156 txflags(tfSellNFToken));
3157
3158 uint256 const offerIssuerToMinter =
3159 keylet::nftoffer(issuer, env.seq(issuer)).key;
3160 env(token::createOffer(issuer, nftokenID0, drops(1)),
3161 token::owner(minter),
3162 token::expiration(expiration));
3163
3164 uint256 const offerBuyerToMinter =
3165 keylet::nftoffer(buyer, env.seq(buyer)).key;
3166 env(token::createOffer(buyer, nftokenID0, drops(1)),
3167 token::owner(minter),
3168 token::expiration(expiration));
3169 env.close();
3170 BEAST_EXPECT(ownerCount(env, issuer) == 1);
3171 BEAST_EXPECT(ownerCount(env, minter) == 3);
3172 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3173
3174 // Test who gets to cancel the offers. Anyone outside of the
3175 // offer-owner/destination pair should not be able to cancel
3176 // unexpired offers.
3177 //
3178 // Note that these are tec responses, so these transactions will
3179 // not be retried by the ledger.
3180 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3181 ter(tecNO_PERMISSION));
3182 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3183 ter(tecNO_PERMISSION));
3184 env.close();
3185 BEAST_EXPECT(lastClose(env) < expiration);
3186 BEAST_EXPECT(ownerCount(env, issuer) == 1);
3187 BEAST_EXPECT(ownerCount(env, minter) == 3);
3188 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3189
3190 // The offer creator can cancel their own unexpired offer.
3191 env(token::cancelOffer(minter, {offerMinterToAnyone}));
3192
3193 // The destination of a sell offer can cancel the NFT owner's
3194 // unexpired offer.
3195 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3196
3197 // Close enough ledgers to get past the expiration.
3198 while (lastClose(env) < expiration)
3199 env.close();
3200
3201 BEAST_EXPECT(ownerCount(env, issuer) == 1);
3202 BEAST_EXPECT(ownerCount(env, minter) == 1);
3203 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3204
3205 // Anyone can cancel expired offers.
3206 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3207 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3208 env.close();
3209 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3210 BEAST_EXPECT(ownerCount(env, minter) == 1);
3211 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3212 }
3213 // Show that:
3214 // 1. An unexpired sell offer with an expiration can be accepted.
3215 // 2. An expired sell offer cannot be accepted and remains
3216 // in ledger after the accept fails.
3217 {
3218 std::uint32_t const expiration = lastClose(env) + 25;
3219
3220 uint256 const offer0 =
3221 keylet::nftoffer(minter, env.seq(minter)).key;
3222 env(token::createOffer(minter, nftokenID0, drops(1)),
3223 token::expiration(expiration),
3224 txflags(tfSellNFToken));
3225
3226 uint256 const offer1 =
3227 keylet::nftoffer(minter, env.seq(minter)).key;
3228 env(token::createOffer(minter, nftokenID1, drops(1)),
3229 token::expiration(expiration),
3230 txflags(tfSellNFToken));
3231 env.close();
3232 BEAST_EXPECT(lastClose(env) < expiration);
3233 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3234 BEAST_EXPECT(ownerCount(env, minter) == 3);
3235 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3236
3237 // Anyone can accept an unexpired sell offer.
3238 env(token::acceptSellOffer(buyer, offer0));
3239
3240 // Close enough ledgers to get past the expiration.
3241 while (lastClose(env) < expiration)
3242 env.close();
3243
3244 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3245 BEAST_EXPECT(ownerCount(env, minter) == 2);
3246 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3247
3248 // No one can accept an expired sell offer.
3249 env(token::acceptSellOffer(buyer, offer1), ter(tecEXPIRED));
3250 env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
3251 env.close();
3252
3253 // The expired sell offer is still in the ledger.
3254 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3255 BEAST_EXPECT(ownerCount(env, minter) == 2);
3256 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3257
3258 // Anyone can cancel the expired sell offer.
3259 env(token::cancelOffer(issuer, {offer1}));
3260 env.close();
3261 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3262 BEAST_EXPECT(ownerCount(env, minter) == 1);
3263 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3264
3265 // Transfer nftokenID0 back to minter so we start the next test in
3266 // a simple place.
3267 uint256 const offerSellBack =
3268 keylet::nftoffer(buyer, env.seq(buyer)).key;
3269 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3270 txflags(tfSellNFToken),
3271 token::destination(minter));
3272 env.close();
3273 env(token::acceptSellOffer(minter, offerSellBack));
3274 env.close();
3275 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3276 BEAST_EXPECT(ownerCount(env, minter) == 1);
3277 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3278 }
3279 // Show that:
3280 // 1. An unexpired buy offer with an expiration can be accepted.
3281 // 2. An expired buy offer cannot be accepted and remains
3282 // in ledger after the accept fails.
3283 {
3284 std::uint32_t const expiration = lastClose(env) + 25;
3285
3286 uint256 const offer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3287 env(token::createOffer(buyer, nftokenID0, drops(1)),
3288 token::owner(minter),
3289 token::expiration(expiration));
3290
3291 uint256 const offer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3292 env(token::createOffer(buyer, nftokenID1, drops(1)),
3293 token::owner(minter),
3294 token::expiration(expiration));
3295 env.close();
3296 BEAST_EXPECT(lastClose(env) < expiration);
3297 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3298 BEAST_EXPECT(ownerCount(env, minter) == 1);
3299 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3300
3301 // An unexpired buy offer can be accepted.
3302 env(token::acceptBuyOffer(minter, offer0));
3303
3304 // Close enough ledgers to get past the expiration.
3305 while (lastClose(env) < expiration)
3306 env.close();
3307
3308 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3309 BEAST_EXPECT(ownerCount(env, minter) == 1);
3310 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3311
3312 // An expired buy offer cannot be accepted.
3313 env(token::acceptBuyOffer(minter, offer1), ter(tecEXPIRED));
3314 env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
3315 env.close();
3316
3317 // The expired buy offer is still in the ledger.
3318 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3319 BEAST_EXPECT(ownerCount(env, minter) == 1);
3320 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3321
3322 // Anyone can cancel the expired buy offer.
3323 env(token::cancelOffer(issuer, {offer1}));
3324 env.close();
3325 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3326 BEAST_EXPECT(ownerCount(env, minter) == 1);
3327 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3328
3329 // Transfer nftokenID0 back to minter so we start the next test in
3330 // a simple place.
3331 uint256 const offerSellBack =
3332 keylet::nftoffer(buyer, env.seq(buyer)).key;
3333 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3334 txflags(tfSellNFToken),
3335 token::destination(minter));
3336 env.close();
3337 env(token::acceptSellOffer(minter, offerSellBack));
3338 env.close();
3339 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3340 BEAST_EXPECT(ownerCount(env, minter) == 1);
3341 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3342 }
3343 // Show that in brokered mode:
3344 // 1. An unexpired sell offer with an expiration can be accepted.
3345 // 2. An expired sell offer cannot be accepted and remains
3346 // in ledger after the accept fails.
3347 {
3348 std::uint32_t const expiration = lastClose(env) + 25;
3349
3350 uint256 const sellOffer0 =
3351 keylet::nftoffer(minter, env.seq(minter)).key;
3352 env(token::createOffer(minter, nftokenID0, drops(1)),
3353 token::expiration(expiration),
3354 txflags(tfSellNFToken));
3355
3356 uint256 const sellOffer1 =
3357 keylet::nftoffer(minter, env.seq(minter)).key;
3358 env(token::createOffer(minter, nftokenID1, drops(1)),
3359 token::expiration(expiration),
3360 txflags(tfSellNFToken));
3361
3362 uint256 const buyOffer0 =
3363 keylet::nftoffer(buyer, env.seq(buyer)).key;
3364 env(token::createOffer(buyer, nftokenID0, drops(1)),
3365 token::owner(minter));
3366
3367 uint256 const buyOffer1 =
3368 keylet::nftoffer(buyer, env.seq(buyer)).key;
3369 env(token::createOffer(buyer, nftokenID1, drops(1)),
3370 token::owner(minter));
3371
3372 env.close();
3373 BEAST_EXPECT(lastClose(env) < expiration);
3374 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3375 BEAST_EXPECT(ownerCount(env, minter) == 3);
3376 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3377
3378 // An unexpired offer can be brokered.
3379 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3380
3381 // Close enough ledgers to get past the expiration.
3382 while (lastClose(env) < expiration)
3383 env.close();
3384
3385 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3386 BEAST_EXPECT(ownerCount(env, minter) == 2);
3387 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3388
3389 // If the sell offer is expired it cannot be brokered.
3390 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3391 ter(tecEXPIRED));
3392 env.close();
3393
3394 // The expired sell offer is still in the ledger.
3395 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3396 BEAST_EXPECT(ownerCount(env, minter) == 2);
3397 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3398
3399 // Anyone can cancel the expired sell offer.
3400 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3401 env.close();
3402 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3403 BEAST_EXPECT(ownerCount(env, minter) == 1);
3404 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3405
3406 // Transfer nftokenID0 back to minter so we start the next test in
3407 // a simple place.
3408 uint256 const offerSellBack =
3409 keylet::nftoffer(buyer, env.seq(buyer)).key;
3410 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3411 txflags(tfSellNFToken),
3412 token::destination(minter));
3413 env.close();
3414 env(token::acceptSellOffer(minter, offerSellBack));
3415 env.close();
3416 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3417 BEAST_EXPECT(ownerCount(env, minter) == 1);
3418 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3419 }
3420 // Show that in brokered mode:
3421 // 1. An unexpired buy offer with an expiration can be accepted.
3422 // 2. An expired buy offer cannot be accepted and remains
3423 // in ledger after the accept fails.
3424 {
3425 std::uint32_t const expiration = lastClose(env) + 25;
3426
3427 uint256 const sellOffer0 =
3428 keylet::nftoffer(minter, env.seq(minter)).key;
3429 env(token::createOffer(minter, nftokenID0, drops(1)),
3430 txflags(tfSellNFToken));
3431
3432 uint256 const sellOffer1 =
3433 keylet::nftoffer(minter, env.seq(minter)).key;
3434 env(token::createOffer(minter, nftokenID1, drops(1)),
3435 txflags(tfSellNFToken));
3436
3437 uint256 const buyOffer0 =
3438 keylet::nftoffer(buyer, env.seq(buyer)).key;
3439 env(token::createOffer(buyer, nftokenID0, drops(1)),
3440 token::expiration(expiration),
3441 token::owner(minter));
3442
3443 uint256 const buyOffer1 =
3444 keylet::nftoffer(buyer, env.seq(buyer)).key;
3445 env(token::createOffer(buyer, nftokenID1, drops(1)),
3446 token::expiration(expiration),
3447 token::owner(minter));
3448
3449 env.close();
3450 BEAST_EXPECT(lastClose(env) < expiration);
3451 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3452 BEAST_EXPECT(ownerCount(env, minter) == 3);
3453 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3454
3455 // An unexpired offer can be brokered.
3456 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3457
3458 // Close enough ledgers to get past the expiration.
3459 while (lastClose(env) < expiration)
3460 env.close();
3461
3462 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3463 BEAST_EXPECT(ownerCount(env, minter) == 2);
3464 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3465
3466 // If the buy offer is expired it cannot be brokered.
3467 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3468 ter(tecEXPIRED));
3469 env.close();
3470
3471 // The expired buy offer is still in the ledger.
3472 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3473 BEAST_EXPECT(ownerCount(env, minter) == 2);
3474 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3475
3476 // Anyone can cancel the expired buy offer.
3477 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3478 env.close();
3479 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3480 BEAST_EXPECT(ownerCount(env, minter) == 1);
3481 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3482
3483 // Transfer nftokenID0 back to minter so we start the next test in
3484 // a simple place.
3485 uint256 const offerSellBack =
3486 keylet::nftoffer(buyer, env.seq(buyer)).key;
3487 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3488 txflags(tfSellNFToken),
3489 token::destination(minter));
3490 env.close();
3491 env(token::acceptSellOffer(minter, offerSellBack));
3492 env.close();
3493 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3494 BEAST_EXPECT(ownerCount(env, minter) == 1);
3495 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3496 }
3497 // Show that in brokered mode:
3498 // 1. An unexpired buy/sell offer pair with an expiration can be
3499 // accepted.
3500 // 2. An expired buy/sell offer pair cannot be accepted and they
3501 // remain in ledger after the accept fails.
3502 {
3503 std::uint32_t const expiration = lastClose(env) + 25;
3504
3505 uint256 const sellOffer0 =
3506 keylet::nftoffer(minter, env.seq(minter)).key;
3507 env(token::createOffer(minter, nftokenID0, drops(1)),
3508 token::expiration(expiration),
3509 txflags(tfSellNFToken));
3510
3511 uint256 const sellOffer1 =
3512 keylet::nftoffer(minter, env.seq(minter)).key;
3513 env(token::createOffer(minter, nftokenID1, drops(1)),
3514 token::expiration(expiration),
3515 txflags(tfSellNFToken));
3516
3517 uint256 const buyOffer0 =
3518 keylet::nftoffer(buyer, env.seq(buyer)).key;
3519 env(token::createOffer(buyer, nftokenID0, drops(1)),
3520 token::expiration(expiration),
3521 token::owner(minter));
3522
3523 uint256 const buyOffer1 =
3524 keylet::nftoffer(buyer, env.seq(buyer)).key;
3525 env(token::createOffer(buyer, nftokenID1, drops(1)),
3526 token::expiration(expiration),
3527 token::owner(minter));
3528
3529 env.close();
3530 BEAST_EXPECT(lastClose(env) < expiration);
3531 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3532 BEAST_EXPECT(ownerCount(env, minter) == 3);
3533 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3534
3535 // Unexpired offers can be brokered.
3536 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3537
3538 // Close enough ledgers to get past the expiration.
3539 while (lastClose(env) < expiration)
3540 env.close();
3541
3542 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3543 BEAST_EXPECT(ownerCount(env, minter) == 2);
3544 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3545
3546 // If the offers are expired they cannot be brokered.
3547 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3548 ter(tecEXPIRED));
3549 env.close();
3550
3551 // The expired offers are still in the ledger.
3552 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3553 BEAST_EXPECT(ownerCount(env, minter) == 2);
3554 BEAST_EXPECT(ownerCount(env, buyer) == 2);
3555
3556 // Anyone can cancel the expired offers.
3557 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3558 env.close();
3559 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3560 BEAST_EXPECT(ownerCount(env, minter) == 1);
3561 BEAST_EXPECT(ownerCount(env, buyer) == 1);
3562
3563 // Transfer nftokenID0 back to minter so we start the next test in
3564 // a simple place.
3565 uint256 const offerSellBack =
3566 keylet::nftoffer(buyer, env.seq(buyer)).key;
3567 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3568 txflags(tfSellNFToken),
3569 token::destination(minter));
3570 env.close();
3571 env(token::acceptSellOffer(minter, offerSellBack));
3572 env.close();
3573 BEAST_EXPECT(ownerCount(env, issuer) == 0);
3574 BEAST_EXPECT(ownerCount(env, minter) == 1);
3575 BEAST_EXPECT(ownerCount(env, buyer) == 0);
3576 }
3577 }
3578
3579 void
3581 {
3582 // Look at offer canceling.
3583 testcase("Cancel offers");
3584
3585 using namespace test::jtx;
3586
3587 Env env{*this, features};
3588
3589 Account const alice("alice");
3590 Account const becky("becky");
3591 Account const minter("minter");
3592 env.fund(XRP(50000), alice, becky, minter);
3593 env.close();
3594
3595 // alice has a minter to see if minters have offer canceling permission.
3596 env(token::setMinter(alice, minter));
3597 env.close();
3598
3599 uint256 const nftokenID =
3600 token::getNextID(env, alice, 0, tfTransferable);
3601 env(token::mint(alice, 0), txflags(tfTransferable));
3602 env.close();
3603
3604 // Anyone can cancel an expired offer.
3605 uint256 const expiredOfferIndex =
3606 keylet::nftoffer(alice, env.seq(alice)).key;
3607
3608 env(token::createOffer(alice, nftokenID, XRP(1000)),
3609 txflags(tfSellNFToken),
3610 token::expiration(lastClose(env) + 13));
3611 env.close();
3612
3613 // The offer has not expired yet, so becky can't cancel it now.
3614 BEAST_EXPECT(ownerCount(env, alice) == 2);
3615 env(token::cancelOffer(becky, {expiredOfferIndex}),
3616 ter(tecNO_PERMISSION));
3617 env.close();
3618
3619 // Close a couple of ledgers and advance the time. Then becky
3620 // should be able to cancel the (now) expired offer.
3621 env.close();
3622 env.close();
3623 env(token::cancelOffer(becky, {expiredOfferIndex}));
3624 env.close();
3625 BEAST_EXPECT(ownerCount(env, alice) == 1);
3626
3627 // Create a couple of offers with a destination. Those offers
3628 // should be cancellable by the creator and the destination.
3629 uint256 const dest1OfferIndex =
3630 keylet::nftoffer(alice, env.seq(alice)).key;
3631
3632 env(token::createOffer(alice, nftokenID, XRP(1000)),
3633 token::destination(becky),
3634 txflags(tfSellNFToken));
3635 env.close();
3636 BEAST_EXPECT(ownerCount(env, alice) == 2);
3637
3638 // Minter can't cancel that offer, but becky (the destination) can.
3639 env(token::cancelOffer(minter, {dest1OfferIndex}),
3640 ter(tecNO_PERMISSION));
3641 env.close();
3642 BEAST_EXPECT(ownerCount(env, alice) == 2);
3643
3644 env(token::cancelOffer(becky, {dest1OfferIndex}));
3645 env.close();
3646 BEAST_EXPECT(ownerCount(env, alice) == 1);
3647
3648 // alice can cancel her own offer, even if becky is the destination.
3649 uint256 const dest2OfferIndex =
3650 keylet::nftoffer(alice, env.seq(alice)).key;
3651
3652 env(token::createOffer(alice, nftokenID, XRP(1000)),
3653 token::destination(becky),
3654 txflags(tfSellNFToken));
3655 env.close();
3656 BEAST_EXPECT(ownerCount(env, alice) == 2);
3657
3658 env(token::cancelOffer(alice, {dest2OfferIndex}));
3659 env.close();
3660 BEAST_EXPECT(ownerCount(env, alice) == 1);
3661
3662 // The issuer has no special permissions regarding offer cancellation.
3663 // Minter creates a token with alice as issuer. alice cannot cancel
3664 // minter's offer.
3665 uint256 const mintersNFTokenID =
3666 token::getNextID(env, alice, 0, tfTransferable);
3667 env(token::mint(minter, 0),
3668 token::issuer(alice),
3669 txflags(tfTransferable));
3670 env.close();
3671
3672 uint256 const minterOfferIndex =
3673 keylet::nftoffer(minter, env.seq(minter)).key;
3674
3675 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3676 txflags(tfSellNFToken));
3677 env.close();
3678 BEAST_EXPECT(ownerCount(env, minter) == 2);
3679
3680 // Nobody other than minter should be able to cancel minter's offer.
3681 env(token::cancelOffer(alice, {minterOfferIndex}),
3682 ter(tecNO_PERMISSION));
3683 env(token::cancelOffer(becky, {minterOfferIndex}),
3684 ter(tecNO_PERMISSION));
3685 env.close();
3686 BEAST_EXPECT(ownerCount(env, minter) == 2);
3687
3688 env(token::cancelOffer(minter, {minterOfferIndex}));
3689 env.close();
3690 BEAST_EXPECT(ownerCount(env, minter) == 1);
3691 }
3692
3693 void
3695 {
3696 // Look at the case where too many offers are passed in a cancel.
3697 testcase("Cancel too many offers");
3698
3699 using namespace test::jtx;
3700
3701 Env env{*this, features};
3702
3703 // We want to maximize the metadata from a cancel offer transaction to
3704 // make sure we don't hit metadata limits. The way we'll do that is:
3705 //
3706 // 1. Generate twice as many separate funded accounts as we have
3707 // offers.
3708 // 2.
3709 // a. One of these accounts mints an NFT with a full URL.
3710 // b. The other account makes an offer that will expire soon.
3711 // 3. After all of these offers have expired, cancel all of the
3712 // expired offers in a single transaction.
3713 //
3714 // I can't think of any way to increase the metadata beyond this,
3715 // but I'm open to ideas.
3716 Account const alice("alice");
3717 env.fund(XRP(1000), alice);
3718 env.close();
3719
3720 std::string const uri(maxTokenURILength, '?');
3721 std::vector<uint256> offerIndexes;
3722 offerIndexes.reserve(maxTokenOfferCancelCount + 1);
3723 for (uint32_t i = 0; i < maxTokenOfferCancelCount + 1; ++i)
3724 {
3725 Account const nftAcct(std::string("nftAcct") + std::to_string(i));
3726 Account const offerAcct(
3727 std::string("offerAcct") + std::to_string(i));
3728 env.fund(XRP(1000), nftAcct, offerAcct);
3729 env.close();
3730
3731 uint256 const nftokenID =
3732 token::getNextID(env, nftAcct, 0, tfTransferable);
3733 env(token::mint(nftAcct, 0),
3734 token::uri(uri),
3735 txflags(tfTransferable));
3736 env.close();
3737
3738 offerIndexes.push_back(
3739 keylet::nftoffer(offerAcct, env.seq(offerAcct)).key);
3740 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3741 token::owner(nftAcct),
3742 token::expiration(lastClose(env) + 5));
3743 env.close();
3744 }
3745
3746 // Close the ledger so the last of the offers expire.
3747 env.close();
3748
3749 // All offers should be in the ledger.
3750 for (uint256 const& offerIndex : offerIndexes)
3751 {
3752 BEAST_EXPECT(env.le(keylet::nftoffer(offerIndex)));
3753 }
3754
3755 // alice attempts to cancel all of the expired offers. There is one
3756 // too many so the request fails.
3757 env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3758 env.close();
3759
3760 // However alice can cancel just one of the offers.
3761 env(token::cancelOffer(alice, {offerIndexes.back()}));
3762 env.close();
3763
3764 // Verify that offer is gone from the ledger.
3765 BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndexes.back())));
3766 offerIndexes.pop_back();
3767
3768 // But alice adds a sell offer to the list...
3769 {
3770 uint256 const nftokenID =
3771 token::getNextID(env, alice, 0, tfTransferable);
3772 env(token::mint(alice, 0),
3773 token::uri(uri),
3774 txflags(tfTransferable));
3775 env.close();
3776
3777 offerIndexes.push_back(keylet::nftoffer(alice, env.seq(alice)).key);
3778 env(token::createOffer(alice, nftokenID, drops(1)),
3779 txflags(tfSellNFToken));
3780 env.close();
3781
3782 // alice's owner count should now to 2 for the nft and the offer.
3783 BEAST_EXPECT(ownerCount(env, alice) == 2);
3784
3785 // Because alice added the sell offer there are still too many
3786 // offers in the list to cancel.
3787 env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3788 env.close();
3789
3790 // alice burns her nft which removes the nft and the offer.
3791 env(token::burn(alice, nftokenID));
3792 env.close();
3793
3794 // If alice's owner count is zero we can see that the offer
3795 // and nft are both gone.
3796 BEAST_EXPECT(ownerCount(env, alice) == 0);
3797 offerIndexes.pop_back();
3798 }
3799
3800 // Now there are few enough offers in the list that they can all
3801 // be cancelled in a single transaction.
3802 env(token::cancelOffer(alice, offerIndexes));
3803 env.close();
3804
3805 // Verify that remaining offers are gone from the ledger.
3806 for (uint256 const& offerIndex : offerIndexes)
3807 {
3808 BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndex)));
3809 }
3810 }
3811
3812 void
3814 {
3815 // Look at the case where too many offers are passed in a cancel.
3816 testcase("Brokered NFT offer accept");
3817
3818 using namespace test::jtx;
3819
3820 {
3821 Env env{*this, features};
3822 auto const baseFee = env.current()->fees().base;
3823
3824 // The most important thing to explore here is the way funds are
3825 // assigned from the buyer to...
3826 // o the Seller,
3827 // o the Broker, and
3828 // o the Issuer (in the case of a transfer fee).
3829
3830 Account const issuer{"issuer"};
3831 Account const minter{"minter"};
3832 Account const buyer{"buyer"};
3833 Account const broker{"broker"};
3834 Account const gw{"gw"};
3835 IOU const gwXAU(gw["XAU"]);
3836
3837 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3838 env.close();
3839
3840 env(trust(issuer, gwXAU(2000)));
3841 env(trust(minter, gwXAU(2000)));
3842 env(trust(buyer, gwXAU(2000)));
3843 env(trust(broker, gwXAU(2000)));
3844 env.close();
3845
3846 env(token::setMinter(issuer, minter));
3847 env.close();
3848
3849 // Lambda to check owner count of all accounts is one.
3850 auto checkOwnerCountIsOne =
3851 [this, &env](
3853 accounts,
3854 int line) {
3855 for (Account const& acct : accounts)
3856 {
3857 if (std::uint32_t ownerCount =
3858 test::jtx::ownerCount(env, acct);
3859 ownerCount != 1)
3860 {
3862 ss << "Account " << acct.human()
3863 << " expected ownerCount == 1. Got "
3864 << ownerCount;
3865 fail(ss.str(), __FILE__, line);
3866 }
3867 }
3868 };
3869
3870 // Lambda that mints an NFT and returns the nftID.
3871 auto mintNFT = [&env, &issuer, &minter](std::uint16_t xferFee = 0) {
3872 uint256 const nftID =
3873 token::getNextID(env, issuer, 0, tfTransferable, xferFee);
3874 env(token::mint(minter, 0),
3875 token::issuer(issuer),
3876 token::xferFee(xferFee),
3877 txflags(tfTransferable));
3878 env.close();
3879 return nftID;
3880 };
3881
3882 // o Seller is selling for zero XRP.
3883 // o Broker charges no fee.
3884 // o No transfer fee.
3885 //
3886 // Since minter is selling for zero the currency must be XRP.
3887 {
3888 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3889
3890 uint256 const nftID = mintNFT();
3891
3892 // minter creates their offer.
3893 uint256 const minterOfferIndex =
3894 keylet::nftoffer(minter, env.seq(minter)).key;
3895 env(token::createOffer(minter, nftID, XRP(0)),
3896 txflags(tfSellNFToken));
3897 env.close();
3898
3899 // buyer creates their offer. Note: a buy offer can never
3900 // offer zero.
3901 uint256 const buyOfferIndex =
3902 keylet::nftoffer(buyer, env.seq(buyer)).key;
3903 env(token::createOffer(buyer, nftID, XRP(1)),
3904 token::owner(minter));
3905 env.close();
3906
3907 auto const minterBalance = env.balance(minter);
3908 auto const buyerBalance = env.balance(buyer);
3909 auto const brokerBalance = env.balance(broker);
3910 auto const issuerBalance = env.balance(issuer);
3911
3912 // Broker charges no brokerFee.
3913 env(token::brokerOffers(
3914 broker, buyOfferIndex, minterOfferIndex));
3915 env.close();
3916
3917 // Note that minter's XRP balance goes up even though they
3918 // requested XRP(0).
3919 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3920 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3921 BEAST_EXPECT(env.balance(broker) == brokerBalance - baseFee);
3922 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3923
3924 // Burn the NFT so the next test starts with a clean state.
3925 env(token::burn(buyer, nftID));
3926 env.close();
3927 }
3928
3929 // o Seller is selling for zero XRP.
3930 // o Broker charges a fee.
3931 // o No transfer fee.
3932 //
3933 // Since minter is selling for zero the currency must be XRP.
3934 {
3935 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3936
3937 uint256 const nftID = mintNFT();
3938
3939 // minter creates their offer.
3940 uint256 const minterOfferIndex =
3941 keylet::nftoffer(minter, env.seq(minter)).key;
3942 env(token::createOffer(minter, nftID, XRP(0)),
3943 txflags(tfSellNFToken));
3944 env.close();
3945
3946 // buyer creates their offer. Note: a buy offer can never
3947 // offer zero.
3948 uint256 const buyOfferIndex =
3949 keylet::nftoffer(buyer, env.seq(buyer)).key;
3950 env(token::createOffer(buyer, nftID, XRP(1)),
3951 token::owner(minter));
3952 env.close();
3953
3954 // Broker attempts to charge a 1.1 XRP brokerFee and fails.
3955 env(token::brokerOffers(
3956 broker, buyOfferIndex, minterOfferIndex),
3957 token::brokerFee(XRP(1.1)),
3959 env.close();
3960
3961 auto const minterBalance = env.balance(minter);
3962 auto const buyerBalance = env.balance(buyer);
3963 auto const brokerBalance = env.balance(broker);
3964 auto const issuerBalance = env.balance(issuer);
3965
3966 // Broker charges a 0.5 XRP brokerFee.
3967 env(token::brokerOffers(
3968 broker, buyOfferIndex, minterOfferIndex),
3969 token::brokerFee(XRP(0.5)));
3970 env.close();
3971
3972 // Note that minter's XRP balance goes up even though they
3973 // requested XRP(0).
3974 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3975 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3976 BEAST_EXPECT(
3977 env.balance(broker) == brokerBalance + XRP(0.5) - baseFee);
3978 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3979
3980 // Burn the NFT so the next test starts with a clean state.
3981 env(token::burn(buyer, nftID));
3982 env.close();
3983 }
3984
3985 // o Seller is selling for zero XRP.
3986 // o Broker charges no fee.
3987 // o 50% transfer fee.
3988 //
3989 // Since minter is selling for zero the currency must be XRP.
3990 {
3991 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3992
3993 uint256 const nftID = mintNFT(maxTransferFee);
3994
3995 // minter creates their offer.
3996 uint256 const minterOfferIndex =
3997 keylet::nftoffer(minter, env.seq(minter)).key;
3998 env(token::createOffer(minter, nftID, XRP(0)),
3999 txflags(tfSellNFToken));
4000 env.close();
4001
4002 // buyer creates their offer. Note: a buy offer can never
4003 // offer zero.
4004 uint256 const buyOfferIndex =
4005 keylet::nftoffer(buyer, env.seq(buyer)).key;
4006 env(token::createOffer(buyer, nftID, XRP(1)),
4007 token::owner(minter));
4008 env.close();
4009
4010 auto const minterBalance = env.balance(minter);
4011 auto const buyerBalance = env.balance(buyer);
4012 auto const brokerBalance = env.balance(broker);
4013 auto const issuerBalance = env.balance(issuer);
4014
4015 // Broker charges no brokerFee.
4016 env(token::brokerOffers(
4017 broker, buyOfferIndex, minterOfferIndex));
4018 env.close();
4019
4020 // Note that minter's XRP balance goes up even though they
4021 // requested XRP(0).
4022 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4023 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4024 BEAST_EXPECT(env.balance(broker) == brokerBalance - baseFee);
4025 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
4026
4027 // Burn the NFT so the next test starts with a clean state.
4028 env(token::burn(buyer, nftID));
4029 env.close();
4030 }
4031
4032 // o Seller is selling for zero XRP.
4033 // o Broker charges 0.5 XRP.
4034 // o 50% transfer fee.
4035 //
4036 // Since minter is selling for zero the currency must be XRP.
4037 {
4038 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4039
4040 uint256 const nftID = mintNFT(maxTransferFee);
4041
4042 // minter creates their offer.
4043 uint256 const minterOfferIndex =
4044 keylet::nftoffer(minter, env.seq(minter)).key;
4045 env(token::createOffer(minter, nftID, XRP(0)),
4046 txflags(tfSellNFToken));
4047 env.close();
4048
4049 // buyer creates their offer. Note: a buy offer can never
4050 // offer zero.
4051 uint256 const buyOfferIndex =
4052 keylet::nftoffer(buyer, env.seq(buyer)).key;
4053 env(token::createOffer(buyer, nftID, XRP(1)),
4054 token::owner(minter));
4055 env.close();
4056
4057 auto const minterBalance = env.balance(minter);
4058 auto const buyerBalance = env.balance(buyer);
4059 auto const brokerBalance = env.balance(broker);
4060 auto const issuerBalance = env.balance(issuer);
4061
4062 // Broker charges a 0.75 XRP brokerFee.
4063 env(token::brokerOffers(
4064 broker, buyOfferIndex, minterOfferIndex),
4065 token::brokerFee(XRP(0.75)));
4066 env.close();
4067
4068 // Note that, with a 50% transfer fee, issuer gets 1/2 of what's
4069 // left _after_ broker takes their fee. minter gets the
4070 // remainder after both broker and minter take their cuts
4071 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
4072 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4073 BEAST_EXPECT(
4074 env.balance(broker) == brokerBalance + XRP(0.75) - baseFee);
4075 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
4076
4077 // Burn the NFT so the next test starts with a clean state.
4078 env(token::burn(buyer, nftID));
4079 env.close();
4080 }
4081
4082 // Lambda to set the balance of all passed in accounts to
4083 // gwXAU(amount).
4084 auto setXAUBalance =
4085 [this, &gw, &gwXAU, &env](
4087 accounts,
4088 int amount,
4089 int line) {
4090 for (Account const& acct : accounts)
4091 {
4092 auto const xauAmt = gwXAU(amount);
4093 auto const balance = env.balance(acct, gwXAU);
4094 if (balance < xauAmt)
4095 {
4096 env(pay(gw, acct, xauAmt - balance));
4097 env.close();
4098 }
4099 else if (balance > xauAmt)
4100 {
4101 env(pay(acct, gw, balance - xauAmt));
4102 env.close();
4103 }
4104 if (env.balance(acct, gwXAU) != xauAmt)
4105 {
4107 ss << "Unable to set " << acct.human()
4108 << " account balance to gwXAU(" << amount << ")";
4109 this->fail(ss.str(), __FILE__, line);
4110 }
4111 }
4112 };
4113
4114 // The buyer and seller have identical amounts and there is no
4115 // transfer fee.
4116 {
4117 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4118 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4119
4120 uint256 const nftID = mintNFT();
4121
4122 // minter creates their offer.
4123 uint256 const minterOfferIndex =
4124 keylet::nftoffer(minter, env.seq(minter)).key;
4125 env(token::createOffer(minter, nftID, gwXAU(1000)),
4126 txflags(tfSellNFToken));
4127 env.close();
4128
4129 {
4130 // buyer creates an offer for more XAU than they currently
4131 // own.
4132 uint256 const buyOfferIndex =
4133 keylet::nftoffer(buyer, env.seq(buyer)).key;
4134 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4135 token::owner(minter));
4136 env.close();
4137
4138 // broker attempts to broker the offers but cannot.
4139 env(token::brokerOffers(
4140 broker, buyOfferIndex, minterOfferIndex),
4142 env.close();
4143
4144 // Cancel buyer's bad offer so the next test starts in a
4145 // clean state.
4146 env(token::cancelOffer(buyer, {buyOfferIndex}));
4147 env.close();
4148 }
4149 {
4150 // buyer creates an offer for less that what minter is
4151 // asking.
4152 uint256 const buyOfferIndex =
4153 keylet::nftoffer(buyer, env.seq(buyer)).key;
4154 env(token::createOffer(buyer, nftID, gwXAU(999)),
4155 token::owner(minter));
4156 env.close();
4157
4158 // broker attempts to broker the offers but cannot.
4159 env(token::brokerOffers(
4160 broker, buyOfferIndex, minterOfferIndex),
4162 env.close();
4163
4164 // Cancel buyer's bad offer so the next test starts in a
4165 // clean state.
4166 env(token::cancelOffer(buyer, {buyOfferIndex}));
4167 env.close();
4168 }
4169
4170 // buyer creates a large enough offer.
4171 uint256 const buyOfferIndex =
4172 keylet::nftoffer(buyer, env.seq(buyer)).key;
4173 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4174 token::owner(minter));
4175 env.close();
4176
4177 // Broker attempts to charge a brokerFee but cannot.
4178 env(token::brokerOffers(
4179 broker, buyOfferIndex, minterOfferIndex),
4180 token::brokerFee(gwXAU(0.1)),
4182 env.close();
4183
4184 // broker charges no brokerFee and succeeds.
4185 env(token::brokerOffers(
4186 broker, buyOfferIndex, minterOfferIndex));
4187 env.close();
4188
4189 BEAST_EXPECT(ownerCount(env, issuer) == 1);
4190 BEAST_EXPECT(ownerCount(env, minter) == 1);
4191 BEAST_EXPECT(ownerCount(env, buyer) == 2);
4192 BEAST_EXPECT(ownerCount(env, broker) == 1);
4193 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4194 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4195 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4196 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4197
4198 // Burn the NFT so the next test starts with a clean state.
4199 env(token::burn(buyer, nftID));
4200 env.close();
4201 }
4202
4203 // seller offers more than buyer is asking.
4204 // There are both transfer and broker fees.
4205 {
4206 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4207 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4208
4209 uint256 const nftID = mintNFT(maxTransferFee);
4210
4211 // minter creates their offer.
4212 uint256 const minterOfferIndex =
4213 keylet::nftoffer(minter, env.seq(minter)).key;
4214 env(token::createOffer(minter, nftID, gwXAU(900)),
4215 txflags(tfSellNFToken));
4216 env.close();
4217 {
4218 // buyer creates an offer for more XAU than they currently
4219 // own.
4220 uint256 const buyOfferIndex =
4221 keylet::nftoffer(buyer, env.seq(buyer)).key;
4222 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4223 token::owner(minter));
4224 env.close();
4225
4226 // broker attempts to broker the offers but cannot.
4227 env(token::brokerOffers(
4228 broker, buyOfferIndex, minterOfferIndex),
4230 env.close();
4231
4232 // Cancel buyer's bad offer so the next test starts in a
4233 // clean state.
4234 env(token::cancelOffer(buyer, {buyOfferIndex}));
4235 env.close();
4236 }
4237 {
4238 // buyer creates an offer for less that what minter is
4239 // asking.
4240 uint256 const buyOfferIndex =
4241 keylet::nftoffer(buyer, env.seq(buyer)).key;
4242 env(token::createOffer(buyer, nftID, gwXAU(899)),
4243 token::owner(minter));
4244 env.close();
4245
4246 // broker attempts to broker the offers but cannot.
4247 env(token::brokerOffers(
4248 broker, buyOfferIndex, minterOfferIndex),
4250 env.close();
4251
4252 // Cancel buyer's bad offer so the next test starts in a
4253 // clean state.
4254 env(token::cancelOffer(buyer, {buyOfferIndex}));
4255 env.close();
4256 }
4257 // buyer creates a large enough offer.
4258 uint256 const buyOfferIndex =
4259 keylet::nftoffer(buyer, env.seq(buyer)).key;
4260 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4261 token::owner(minter));
4262 env.close();
4263
4264 // Broker attempts to charge a brokerFee larger than the
4265 // difference between the two offers but cannot.
4266 env(token::brokerOffers(
4267 broker, buyOfferIndex, minterOfferIndex),
4268 token::brokerFee(gwXAU(101)),
4270 env.close();
4271
4272 // broker charges the full difference between the two offers and
4273 // succeeds.
4274 env(token::brokerOffers(
4275 broker, buyOfferIndex, minterOfferIndex),
4276 token::brokerFee(gwXAU(100)));
4277 env.close();
4278
4279 BEAST_EXPECT(ownerCount(env, issuer) == 1);
4280 BEAST_EXPECT(ownerCount(env, minter) == 1);
4281 BEAST_EXPECT(ownerCount(env, buyer) == 2);
4282 BEAST_EXPECT(ownerCount(env, broker) == 1);
4283 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4284 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4285 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4286 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4287
4288 // Burn the NFT so the next test starts with a clean state.
4289 env(token::burn(buyer, nftID));
4290 env.close();
4291 }
4292 // seller offers more than buyer is asking.
4293 // There are both transfer and broker fees, but broker takes less
4294 // than the maximum.
4295 {
4296 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4297 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4298
4299 uint256 const nftID = mintNFT(maxTransferFee / 2); // 25%
4300
4301 // minter creates their offer.
4302 uint256 const minterOfferIndex =
4303 keylet::nftoffer(minter, env.seq(minter)).key;
4304 env(token::createOffer(minter, nftID, gwXAU(900)),
4305 txflags(tfSellNFToken));
4306 env.close();
4307
4308 // buyer creates a large enough offer.
4309 uint256 const buyOfferIndex =
4310 keylet::nftoffer(buyer, env.seq(buyer)).key;
4311 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4312 token::owner(minter));
4313 env.close();
4314
4315 // broker charges half difference between the two offers and
4316 // succeeds. 25% of the remaining difference goes to issuer.
4317 // The rest goes to minter.
4318 env(token::brokerOffers(
4319 broker, buyOfferIndex, minterOfferIndex),
4320 token::brokerFee(gwXAU(50)));
4321 env.close();
4322
4323 BEAST_EXPECT(ownerCount(env, issuer) == 1);
4324 BEAST_EXPECT(ownerCount(env, minter) == 1);
4325 BEAST_EXPECT(ownerCount(env, buyer) == 2);
4326 BEAST_EXPECT(ownerCount(env, broker) == 1);
4327 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4328 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4329 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4330 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4331
4332 // Burn the NFT so the next test starts with a clean state.
4333 env(token::burn(buyer, nftID));
4334 env.close();
4335 }
4336 // Broker has a balance less than the seller offer
4337 {
4338 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4339 setXAUBalance({issuer, minter, buyer}, 1000, __LINE__);
4340 setXAUBalance({broker}, 500, __LINE__);
4341 uint256 const nftID = mintNFT(maxTransferFee / 2); // 25%
4342
4343 // minter creates their offer.
4344 uint256 const minterOfferIndex =
4345 keylet::nftoffer(minter, env.seq(minter)).key;
4346 env(token::createOffer(minter, nftID, gwXAU(900)),
4347 txflags(tfSellNFToken));
4348 env.close();
4349
4350 // buyer creates a large enough offer.
4351 uint256 const buyOfferIndex =
4352 keylet::nftoffer(buyer, env.seq(buyer)).key;
4353 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4354 token::owner(minter));
4355 env.close();
4356
4357 env(token::brokerOffers(
4358 broker, buyOfferIndex, minterOfferIndex),
4359 token::brokerFee(gwXAU(50)));
4360 env.close();
4361 BEAST_EXPECT(ownerCount(env, issuer) == 1);
4362 BEAST_EXPECT(ownerCount(env, minter) == 1);
4363 BEAST_EXPECT(ownerCount(env, buyer) == 2);
4364 BEAST_EXPECT(ownerCount(env, broker) == 1);
4365 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4366 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4367 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4368 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(550));
4369
4370 // Burn the NFT so the next test starts with a clean state.
4371 env(token::burn(buyer, nftID));
4372 env.close();
4373 }
4374 }
4375 }
4376
4377 void
4379 {
4380 // Verify the Owner field of an offer behaves as expected.
4381 testcase("NFToken offer owner");
4382
4383 using namespace test::jtx;
4384
4385 Env env{*this, features};
4386
4387 Account const issuer{"issuer"};
4388 Account const buyer1{"buyer1"};
4389 Account const buyer2{"buyer2"};
4390 env.fund(XRP(10000), issuer, buyer1, buyer2);
4391 env.close();
4392
4393 // issuer creates an NFT.
4394 uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4395 env(token::mint(issuer, 0u), txflags(tfTransferable));
4396 env.close();
4397
4398 // Prove that issuer now owns nftId.
4399 BEAST_EXPECT(nftCount(env, issuer) == 1);
4400 BEAST_EXPECT(nftCount(env, buyer1) == 0);
4401 BEAST_EXPECT(nftCount(env, buyer2) == 0);
4402
4403 // Both buyer1 and buyer2 create buy offers for nftId.
4404 uint256 const buyer1OfferIndex =
4405 keylet::nftoffer(buyer1, env.seq(buyer1)).key;
4406 env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4407 uint256 const buyer2OfferIndex =
4408 keylet::nftoffer(buyer2, env.seq(buyer2)).key;
4409 env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4410 env.close();
4411
4412 // Lambda that counts the number of buy offers for a given NFT.
4413 auto nftBuyOfferCount = [&env](uint256 const& nftId) -> std::size_t {
4414 // We know that in this case not very many offers will be
4415 // returned, so we skip the marker stuff.
4416 Json::Value params;
4417 params[jss::nft_id] = to_string(nftId);
4418 Json::Value buyOffers =
4419 env.rpc("json", "nft_buy_offers", to_string(params));
4420
4421 if (buyOffers.isMember(jss::result) &&
4422 buyOffers[jss::result].isMember(jss::offers))
4423 return buyOffers[jss::result][jss::offers].size();
4424
4425 return 0;
4426 };
4427
4428 // Show there are two buy offers for nftId.
4429 BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4430
4431 // issuer accepts buyer1's offer.
4432 env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4433 env.close();
4434
4435 // Prove that buyer1 now owns nftId.
4436 BEAST_EXPECT(nftCount(env, issuer) == 0);
4437 BEAST_EXPECT(nftCount(env, buyer1) == 1);
4438 BEAST_EXPECT(nftCount(env, buyer2) == 0);
4439
4440 // buyer1's offer was consumed, but buyer2's offer is still in the
4441 // ledger.
4442 BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4443
4444 // buyer1 can now accept buyer2's offer, even though buyer2's
4445 // NFTokenCreateOffer transaction specified the NFT Owner as issuer.
4446 env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4447 env.close();
4448
4449 // Prove that buyer2 now owns nftId.
4450 BEAST_EXPECT(nftCount(env, issuer) == 0);
4451 BEAST_EXPECT(nftCount(env, buyer1) == 0);
4452 BEAST_EXPECT(nftCount(env, buyer2) == 1);
4453
4454 // All of the NFTokenOffers are now consumed.
4455 BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4456 }
4457
4458 void
4460 {
4461 // Make sure all NFToken transactions work with tickets.
4462 testcase("NFToken transactions with tickets");
4463
4464 using namespace test::jtx;
4465
4466 Env env{*this, features};
4467
4468 Account const issuer{"issuer"};
4469 Account const buyer{"buyer"};
4470 env.fund(XRP(10000), issuer, buyer);
4471 env.close();
4472
4473 // issuer and buyer grab enough tickets for all of the following
4474 // transactions. Note that once the tickets are acquired issuer's
4475 // and buyer's account sequence numbers should not advance.
4476 std::uint32_t issuerTicketSeq{env.seq(issuer) + 1};
4477 env(ticket::create(issuer, 10));
4478 env.close();
4479 std::uint32_t const issuerSeq{env.seq(issuer)};
4480 BEAST_EXPECT(ticketCount(env, issuer) == 10);
4481
4482 std::uint32_t buyerTicketSeq{env.seq(buyer) + 1};
4483 env(ticket::create(buyer, 10));
4484 env.close();
4485 std::uint32_t const buyerSeq{env.seq(buyer)};
4486 BEAST_EXPECT(ticketCount(env, buyer) == 10);
4487
4488 // NFTokenMint
4489 BEAST_EXPECT(ownerCount(env, issuer) == 10);
4490 uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4491 env(token::mint(issuer, 0u),
4492 txflags(tfTransferable),
4493 ticket::use(issuerTicketSeq++));
4494 env.close();
4495 BEAST_EXPECT(ownerCount(env, issuer) == 10);
4496 BEAST_EXPECT(ticketCount(env, issuer) == 9);
4497
4498 // NFTokenCreateOffer
4499 BEAST_EXPECT(ownerCount(env, buyer) == 10);
4500 uint256 const offerIndex0 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4501 env(token::createOffer(buyer, nftId, XRP(1)),
4502 token::owner(issuer),
4503 ticket::use(buyerTicketSeq++));
4504 env.close();
4505 BEAST_EXPECT(ownerCount(env, buyer) == 10);
4506 BEAST_EXPECT(ticketCount(env, buyer) == 9);
4507
4508 // NFTokenCancelOffer
4509 env(token::cancelOffer(buyer, {offerIndex0}),
4510 ticket::use(buyerTicketSeq++));
4511 env.close();
4512 BEAST_EXPECT(ownerCount(env, buyer) == 8);
4513 BEAST_EXPECT(ticketCount(env, buyer) == 8);
4514
4515 // NFTokenCreateOffer. buyer tries again.
4516 uint256 const offerIndex1 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4517 env(token::createOffer(buyer, nftId, XRP(2)),
4518 token::owner(issuer),
4519 ticket::use(buyerTicketSeq++));
4520 env.close();
4521 BEAST_EXPECT(ownerCount(env, buyer) == 8);
4522 BEAST_EXPECT(ticketCount(env, buyer) == 7);
4523
4524 // NFTokenAcceptOffer. issuer accepts buyer's offer.
4525 env(token::acceptBuyOffer(issuer, offerIndex1),
4526 ticket::use(issuerTicketSeq++));
4527 env.close();
4528 BEAST_EXPECT(ownerCount(env, issuer) == 8);
4529 BEAST_EXPECT(ownerCount(env, buyer) == 8);
4530 BEAST_EXPECT(ticketCount(env, issuer) == 8);
4531
4532 // NFTokenBurn. buyer burns the token they just bought.
4533 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4534 env.close();
4535 BEAST_EXPECT(ownerCount(env, issuer) == 8);
4536 BEAST_EXPECT(ownerCount(env, buyer) == 6);
4537 BEAST_EXPECT(ticketCount(env, buyer) == 6);
4538
4539 // Verify that the account sequence numbers did not advance.
4540 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4541 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4542 }
4543
4544 void
4546 {
4547 // Account deletion rules with NFTs:
4548 // 1. An account holding one or more NFT offers may be deleted.
4549 // 2. An NFT issuer with any NFTs they have issued still in the
4550 // ledger may not be deleted.
4551 // 3. An account holding one or more NFTs may not be deleted.
4552 testcase("NFToken delete account");
4553
4554 using namespace test::jtx;
4555
4556 Env env{*this, features};
4557
4558 Account const issuer{"issuer"};
4559 Account const minter{"minter"};
4560 Account const becky{"becky"};
4561 Account const carla{"carla"};
4562 Account const daria{"daria"};
4563
4564 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4565 env.close();
4566
4567 // Allow enough ledgers to pass so any of these accounts can be deleted.
4568 for (int i = 0; i < 300; ++i)
4569 env.close();
4570
4571 env(token::setMinter(issuer, minter));
4572 env.close();
4573
4574 uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4575 env(token::mint(minter, 0u),
4576 token::issuer(issuer),
4577 txflags(tfTransferable));
4578 env.close();
4579
4580 // At the moment issuer and minter cannot delete themselves.
4581 // o issuer has an issued NFT in the ledger.
4582 // o minter owns an NFT.
4583 env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4584 env(acctdelete(minter, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4585 env.close();
4586
4587 // Let enough ledgers pass so the account delete transactions are
4588 // not retried.
4589 for (int i = 0; i < 15; ++i)
4590 env.close();
4591
4592 // becky and carla create offers for minter's NFT.
4593 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4594 env.close();
4595
4596 uint256 const carlaOfferIndex =
4597 keylet::nftoffer(carla, env.seq(carla)).key;
4598 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4599 env.close();
4600
4601 // It should be possible for becky to delete herself, even though
4602 // becky has an active NFT offer.
4603 env(acctdelete(becky, daria), fee(XRP(50)));
4604 env.close();
4605
4606 // minter accepts carla's offer.
4607 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4608 env.close();
4609
4610 // Now it should be possible for minter to delete themselves since
4611 // they no longer own an NFT.
4612 env(acctdelete(minter, daria), fee(XRP(50)));
4613 env.close();
4614
4615 // 1. issuer cannot delete themselves because they issued an NFT that
4616 // is still in the ledger.
4617 // 2. carla owns an NFT, so she cannot delete herself.
4618 env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4619 env(acctdelete(carla, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4620 env.close();
4621
4622 // Let enough ledgers pass so the account delete transactions are
4623 // not retried.
4624 for (int i = 0; i < 15; ++i)
4625 env.close();
4626
4627 // carla burns her NFT. Since issuer's NFT is no longer in the
4628 // ledger, both issuer and carla can delete themselves.
4629 env(token::burn(carla, nftId));
4630 env.close();
4631
4632 env(acctdelete(issuer, daria), fee(XRP(50)));
4633 env(acctdelete(carla, daria), fee(XRP(50)));
4634 env.close();
4635 }
4636
4637 void
4639 {
4640 testcase("nft_buy_offers and nft_sell_offers");
4641
4642 // The default limit on returned NFToken offers is 250, so we need
4643 // to produce more than 250 offers of each kind in order to exercise
4644 // the marker.
4645
4646 // Fortunately there's nothing in the rules that says an account
4647 // can't hold more than one offer for the same NFT. So we only
4648 // need two accounts to generate the necessary offers.
4649 using namespace test::jtx;
4650
4651 Env env{*this, features};
4652
4653 Account const issuer{"issuer"};
4654 Account const buyer{"buyer"};
4655
4656 // A lot of offers requires a lot for reserve.
4657 env.fund(XRP(1000000), issuer, buyer);
4658 env.close();
4659
4660 // Create an NFT that we'll make offers for.
4661 uint256 const nftID{token::getNextID(env, issuer, 0u, tfTransferable)};
4662 env(token::mint(issuer, 0), txflags(tfTransferable));
4663 env.close();
4664
4665 // A lambda that validates nft_XXX_offers query responses.
4666 auto checkOffers = [this, &env, &nftID](
4667 char const* request,
4668 int expectCount,
4669 int expectMarkerCount,
4670 int line) {
4671 int markerCount = 0;
4672 Json::Value allOffers(Json::arrayValue);
4673 std::string marker;
4674
4675 // The do/while collects results until no marker is returned.
4676 do
4677 {
4678 Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4679 Json::Value params;
4680 params[jss::nft_id] = to_string(nftID);
4681
4682 if (!marker.empty())
4683 params[jss::marker] = marker;
4684 return env.rpc("json", request, to_string(params));
4685 }();
4686
4687 // If there are no offers for the NFT we get an error
4688 if (expectCount == 0)
4689 {
4690 if (expect(
4691 nftOffers.isMember(jss::result),
4692 "expected \"result\"",
4693 __FILE__,
4694 line))
4695 {
4696 if (expect(
4697 nftOffers[jss::result].isMember(jss::error),
4698 "expected \"error\"",
4699 __FILE__,
4700 line))
4701 {
4702 expect(
4703 nftOffers[jss::result][jss::error].asString() ==
4704 "objectNotFound",
4705 "expected \"objectNotFound\"",
4706 __FILE__,
4707 line);
4708 }
4709 }
4710 break;
4711 }
4712
4713 marker.clear();
4714 if (expect(
4715 nftOffers.isMember(jss::result),
4716 "expected \"result\"",
4717 __FILE__,
4718 line))
4719 {
4720 Json::Value& result = nftOffers[jss::result];
4721
4722 if (result.isMember(jss::marker))
4723 {
4724 ++markerCount;
4725 marker = result[jss::marker].asString();
4726 }
4727
4728 if (expect(
4729 result.isMember(jss::offers),
4730 "expected \"offers\"",
4731 __FILE__,
4732 line))
4733 {
4734 Json::Value& someOffers = result[jss::offers];
4735 for (std::size_t i = 0; i < someOffers.size(); ++i)
4736 allOffers.append(someOffers[i]);
4737 }
4738 }
4739 } while (!marker.empty());
4740
4741 // Verify the contents of allOffers makes sense.
4742 expect(
4743 allOffers.size() == expectCount,
4744 "Unexpected returned offer count",
4745 __FILE__,
4746 line);
4747 expect(
4748 markerCount == expectMarkerCount,
4749 "Unexpected marker count",
4750 __FILE__,
4751 line);
4752 std::optional<int> globalFlags;
4753 std::set<std::string> offerIndexes;
4754 std::set<std::string> amounts;
4755 for (Json::Value const& offer : allOffers)
4756 {
4757 // The flags on all found offers should be the same.
4758 if (!globalFlags)
4759 globalFlags = offer[jss::flags].asInt();
4760
4761 expect(
4762 *globalFlags == offer[jss::flags].asInt(),
4763 "Inconsistent flags returned",
4764 __FILE__,
4765 line);
4766
4767 // The test conditions should produce unique indexes and
4768 // amounts for all offers.
4769 offerIndexes.insert(offer[jss::nft_offer_index].asString());
4770 amounts.insert(offer[jss::amount].asString());
4771 }
4772
4773 expect(
4774 offerIndexes.size() == expectCount,
4775 "Duplicate indexes returned?",
4776 __FILE__,
4777 line);
4778 expect(
4779 amounts.size() == expectCount,
4780 "Duplicate amounts returned?",
4781 __FILE__,
4782 line);
4783 };
4784
4785 // There are no sell offers.
4786 checkOffers("nft_sell_offers", 0, false, __LINE__);
4787
4788 // A lambda that generates sell offers.
4789 STAmount sellPrice = XRP(0);
4790 auto makeSellOffers =
4791 [&env, &issuer, &nftID, &sellPrice](STAmount const& limit) {
4792 // Save a little test time by not closing too often.
4793 int offerCount = 0;
4794 while (sellPrice < limit)
4795 {
4796 sellPrice += XRP(1);
4797 env(token::createOffer(issuer, nftID, sellPrice),
4798 txflags(tfSellNFToken));
4799 if (++offerCount % 10 == 0)
4800 env.close();
4801 }
4802 env.close();
4803 };
4804
4805 // There is one sell offer.
4806 makeSellOffers(XRP(1));
4807 checkOffers("nft_sell_offers", 1, 0, __LINE__);
4808
4809 // There are 250 sell offers.
4810 makeSellOffers(XRP(250));
4811 checkOffers("nft_sell_offers", 250, 0, __LINE__);
4812
4813 // There are 251 sell offers.
4814 makeSellOffers(XRP(251));
4815 checkOffers("nft_sell_offers", 251, 1, __LINE__);
4816
4817 // There are 500 sell offers.
4818 makeSellOffers(XRP(500));
4819 checkOffers("nft_sell_offers", 500, 1, __LINE__);
4820
4821 // There are 501 sell offers.
4822 makeSellOffers(XRP(501));
4823 checkOffers("nft_sell_offers", 501, 2, __LINE__);
4824
4825 // There are no buy offers.
4826 checkOffers("nft_buy_offers", 0, 0, __LINE__);
4827
4828 // A lambda that generates buy offers.
4829 STAmount buyPrice = XRP(0);
4830 auto makeBuyOffers =
4831 [&env, &buyer, &issuer, &nftID, &buyPrice](STAmount const& limit) {
4832 // Save a little test time by not closing too often.
4833 int offerCount = 0;
4834 while (buyPrice < limit)
4835 {
4836 buyPrice += XRP(1);
4837 env(token::createOffer(buyer, nftID, buyPrice),
4838 token::owner(issuer));
4839 if (++offerCount % 10 == 0)
4840 env.close();
4841 }
4842 env.close();
4843 };
4844
4845 // There is one buy offer;
4846 makeBuyOffers(XRP(1));
4847 checkOffers("nft_buy_offers", 1, 0, __LINE__);
4848
4849 // There are 250 buy offers.
4850 makeBuyOffers(XRP(250));
4851 checkOffers("nft_buy_offers", 250, 0, __LINE__);
4852
4853 // There are 251 buy offers.
4854 makeBuyOffers(XRP(251));
4855 checkOffers("nft_buy_offers", 251, 1, __LINE__);
4856
4857 // There are 500 buy offers.
4858 makeBuyOffers(XRP(500));
4859 checkOffers("nft_buy_offers", 500, 1, __LINE__);
4860
4861 // There are 501 buy offers.
4862 makeBuyOffers(XRP(501));
4863 checkOffers("nft_buy_offers", 501, 2, __LINE__);
4864 }
4865
4866 void
4868 {
4869 using namespace test::jtx;
4870
4871 testcase("NFTokenNegOffer");
4872
4873 Account const issuer{"issuer"};
4874 Account const buyer{"buyer"};
4875 Account const gw{"gw"};
4876 IOU const gwXAU(gw["XAU"]);
4877
4878 {
4879 Env env{*this, features};
4880
4881 env.fund(XRP(1000000), issuer, buyer, gw);
4882 env.close();
4883
4884 env(trust(issuer, gwXAU(2000)));
4885 env(trust(buyer, gwXAU(2000)));
4886 env.close();
4887
4888 env(pay(gw, issuer, gwXAU(1000)));
4889 env(pay(gw, buyer, gwXAU(1000)));
4890 env.close();
4891
4892 // Create an NFT that we'll make XRP offers for.
4893 uint256 const nftID0{
4894 token::getNextID(env, issuer, 0u, tfTransferable)};
4895 env(token::mint(issuer, 0), txflags(tfTransferable));
4896 env.close();
4897
4898 // Create an NFT that we'll make IOU offers for.
4899 uint256 const nftID1{
4900 token::getNextID(env, issuer, 1u, tfTransferable)};
4901 env(token::mint(issuer, 1), txflags(tfTransferable));
4902 env.close();
4903
4904 TER const offerCreateTER = temBAD_AMOUNT;
4905
4906 // Make offers with negative amounts for the NFTs
4907 uint256 const sellNegXrpOfferIndex =
4908 keylet::nftoffer(issuer, env.seq(issuer)).key;
4909 env(token::createOffer(issuer, nftID0, XRP(-2)),
4910 txflags(tfSellNFToken),
4911 ter(offerCreateTER));
4912 env.close();
4913
4914 uint256 const sellNegIouOfferIndex =
4915 keylet::nftoffer(issuer, env.seq(issuer)).key;
4916 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4917 txflags(tfSellNFToken),
4918 ter(offerCreateTER));
4919 env.close();
4920
4921 uint256 const buyNegXrpOfferIndex =
4922 keylet::nftoffer(buyer, env.seq(buyer)).key;
4923 env(token::createOffer(buyer, nftID0, XRP(-1)),
4924 token::owner(issuer),
4925 ter(offerCreateTER));
4926 env.close();
4927
4928 uint256 const buyNegIouOfferIndex =
4929 keylet::nftoffer(buyer, env.seq(buyer)).key;
4930 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4931 token::owner(issuer),
4932 ter(offerCreateTER));
4933 env.close();
4934
4935 {
4936 // Now try to accept the offers.
4937 TER const offerAcceptTER = tecOBJECT_NOT_FOUND;
4938
4939 // Sell offers.
4940 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4941 ter(offerAcceptTER));
4942 env.close();
4943 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4944 ter(offerAcceptTER));
4945 env.close();
4946
4947 // Buy offers.
4948 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
4949 ter(offerAcceptTER));
4950 env.close();
4951 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
4952 ter(offerAcceptTER));
4953 env.close();
4954 }
4955 {
4956 TER const offerAcceptTER = tecOBJECT_NOT_FOUND;
4957
4958 // Brokered offers.
4959 env(token::brokerOffers(
4960 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
4961 ter(offerAcceptTER));
4962 env.close();
4963 env(token::brokerOffers(
4964 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
4965 ter(offerAcceptTER));
4966 env.close();
4967 }
4968 }
4969
4970 {
4971 // Test buy offers with a destination.
4972 Env env{*this, features};
4973
4974 env.fund(XRP(1000000), issuer, buyer);
4975
4976 // Create an NFT that we'll make offers for.
4977 uint256 const nftID{
4978 token::getNextID(env, issuer, 0u, tfTransferable)};
4979 env(token::mint(issuer, 0), txflags(tfTransferable));
4980 env.close();
4981
4982 TER const offerCreateTER = tesSUCCESS;
4983
4984 env(token::createOffer(buyer, nftID, drops(1)),
4985 token::owner(issuer),
4986 token::destination(issuer),
4987 ter(offerCreateTER));
4988 env.close();
4989 }
4990 }
4991
4992 void
4994 {
4995 using namespace test::jtx;
4996
4997 testcase("Payments with IOU transfer fees");
4998
4999 {
5000 Env env{*this, features};
5001
5002 Account const minter{"minter"};
5003 Account const secondarySeller{"seller"};
5004 Account const buyer{"buyer"};
5005 Account const gw{"gateway"};
5006 Account const broker{"broker"};
5007 IOU const gwXAU(gw["XAU"]);
5008 IOU const gwXPB(gw["XPB"]);
5009
5010 env.fund(XRP(1000), gw, minter, secondarySeller, buyer, broker);
5011 env.close();
5012
5013 env(trust(minter, gwXAU(2000)));
5014 env(trust(secondarySeller, gwXAU(2000)));
5015 env(trust(broker, gwXAU(10000)));
5016 env(trust(buyer, gwXAU(2000)));
5017 env(trust(buyer, gwXPB(2000)));
5018 env.close();
5019
5020 // The IOU issuer has a 2% transfer rate
5021 env(rate(gw, 1.02));
5022 env.close();
5023
5024 auto expectInitialState = [this,
5025 &env,
5026 &buyer,
5027 &minter,
5028 &secondarySeller,
5029 &broker,
5030 &gw,
5031 &gwXAU,
5032 &gwXPB]() {
5033 // Buyer should have XAU 1000, XPB 0
5034 // Minter should have XAU 0, XPB 0
5035 // Secondary seller should have XAU 0, XPB 0
5036 // Broker should have XAU 5000, XPB 0
5037 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000));
5038 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(0));
5039 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(0));
5040 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(0));
5041 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(0));
5042 BEAST_EXPECT(env.balance(secondarySeller, gwXPB) == gwXPB(0));
5043 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5000));
5044 BEAST_EXPECT(env.balance(broker, gwXPB) == gwXPB(0));
5045 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-1000));
5046 BEAST_EXPECT(env.balance(gw, buyer["XPB"]) == gwXPB(0));
5047 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(0));
5048 BEAST_EXPECT(env.balance(gw, minter["XPB"]) == gwXPB(0));
5049 BEAST_EXPECT(
5050 env.balance(gw, secondarySeller["XAU"]) == gwXAU(0));
5051 BEAST_EXPECT(
5052 env.balance(gw, secondarySeller["XPB"]) == gwXPB(0));
5053 BEAST_EXPECT(env.balance(gw, broker["XAU"]) == gwXAU(-5000));
5054 BEAST_EXPECT(env.balance(gw, broker["XPB"]) == gwXPB(0));
5055 };
5056
5057 auto reinitializeTrustLineBalances = [&expectInitialState,
5058 &env,
5059 &buyer,
5060 &minter,
5061 &secondarySeller,
5062 &broker,
5063 &gw,
5064 &gwXAU,
5065 &gwXPB]() {
5066 if (auto const difference =
5067 gwXAU(1000) - env.balance(buyer, gwXAU);
5068 difference > gwXAU(0))
5069 env(pay(gw, buyer, difference));
5070 if (env.balance(buyer, gwXPB) > gwXPB(0))
5071 env(pay(buyer, gw, env.balance(buyer, gwXPB)));
5072 if (env.balance(minter, gwXAU) > gwXAU(0))
5073 env(pay(minter, gw, env.balance(minter, gwXAU)));
5074 if (env.balance(minter, gwXPB) > gwXPB(0))
5075 env(pay(minter, gw, env.balance(minter, gwXPB)));
5076 if (env.balance(secondarySeller, gwXAU) > gwXAU(0))
5077 env(
5078 pay(secondarySeller,
5079 gw,
5080 env.balance(secondarySeller, gwXAU)));
5081 if (env.balance(secondarySeller, gwXPB) > gwXPB(0))
5082 env(
5083 pay(secondarySeller,
5084 gw,
5085 env.balance(secondarySeller, gwXPB)));
5086 auto brokerDiff = gwXAU(5000) - env.balance(broker, gwXAU);
5087 if (brokerDiff > gwXAU(0))
5088 env(pay(gw, broker, brokerDiff));
5089 else if (brokerDiff < gwXAU(0))
5090 {
5091 brokerDiff.negate();
5092 env(pay(broker, gw, brokerDiff));
5093 }
5094 if (env.balance(broker, gwXPB) > gwXPB(0))
5095 env(pay(broker, gw, env.balance(broker, gwXPB)));
5096 env.close();
5097 expectInitialState();
5098 };
5099
5100 auto mintNFT = [&env](Account const& minter, int transferFee = 0) {
5101 uint256 const nftID = token::getNextID(
5102 env, minter, 0, tfTransferable, transferFee);
5103 env(token::mint(minter),
5104 token::xferFee(transferFee),
5105 txflags(tfTransferable));
5106 env.close();
5107 return nftID;
5108 };
5109
5110 auto createBuyOffer =
5111 [&env](
5112 Account const& offerer,
5113 Account const& owner,
5114 uint256 const& nftID,
5115 STAmount const& amount,
5116 std::optional<TER const> const terCode = {}) {
5117 uint256 const offerID =
5118 keylet::nftoffer(offerer, env.seq(offerer)).key;
5119 env(token::createOffer(offerer, nftID, amount),
5120 token::owner(owner),
5121 terCode ? ter(*terCode)
5122 : ter(static_cast<TER>(tesSUCCESS)));
5123 env.close();
5124 return offerID;
5125 };
5126
5127 auto createSellOffer =
5128 [&env](
5129 Account const& offerer,
5130 uint256 const& nftID,
5131 STAmount const& amount,
5132 std::optional<TER const> const terCode = {}) {
5133 uint256 const offerID =
5134 keylet::nftoffer(offerer, env.seq(offerer)).key;
5135 env(token::createOffer(offerer, nftID, amount),
5136 txflags(tfSellNFToken),
5137 terCode ? ter(*terCode)
5138 : ter(static_cast<TER>(tesSUCCESS)));
5139 env.close();
5140 return offerID;
5141 };
5142
5143 {
5144 // Buyer attempts to send 100% of their balance of an IOU
5145 // (sellside)
5146 reinitializeTrustLineBalances();
5147 auto const nftID = mintNFT(minter);
5148 auto const offerID =
5149 createSellOffer(minter, nftID, gwXAU(1000));
5150 TER const sellTER = tecINSUFFICIENT_FUNDS;
5151 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5152 env.close();
5153
5154 expectInitialState();
5155 }
5156 {
5157 // Buyer attempts to send 100% of their balance of an IOU
5158 // (buyside)
5159 reinitializeTrustLineBalances();
5160 auto const nftID = mintNFT(minter);
5161 auto const offerID =
5162 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5163 TER const sellTER = tecINSUFFICIENT_FUNDS;
5164 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5165 env.close();
5166
5167 expectInitialState();
5168 }
5169 {
5170 // Buyer attempts to send an amount less than 100% of their
5171 // balance of an IOU, but such that the addition of the transfer
5172 // fee would be greater than the buyer's balance (sellside)
5173 reinitializeTrustLineBalances();
5174 auto const nftID = mintNFT(minter);
5175 auto const offerID = createSellOffer(minter, nftID, gwXAU(995));
5176 TER const sellTER = tecINSUFFICIENT_FUNDS;
5177 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5178 env.close();
5179
5180 expectInitialState();
5181 }
5182 {
5183 // Buyer attempts to send an amount less than 100% of their
5184 // balance of an IOU, but such that the addition of the transfer
5185 // fee would be greater than the buyer's balance (buyside)
5186 reinitializeTrustLineBalances();
5187 auto const nftID = mintNFT(minter);
5188 auto const offerID =
5189 createBuyOffer(buyer, minter, nftID, gwXAU(995));
5190 TER const sellTER = tecINSUFFICIENT_FUNDS;
5191 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5192 env.close();
5193
5194 expectInitialState();
5195 }
5196 {
5197 // Buyer attempts to send an amount less than 100% of their
5198 // balance of an IOU with a transfer fee, and such that the
5199 // addition of the transfer fee is still less than their balance
5200 // (sellside)
5201 reinitializeTrustLineBalances();
5202 auto const nftID = mintNFT(minter);
5203 auto const offerID = createSellOffer(minter, nftID, gwXAU(900));
5204 env(token::acceptSellOffer(buyer, offerID));
5205 env.close();
5206
5207 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5208 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5209 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-900));
5210 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-82));
5211 }
5212 {
5213 // Buyer attempts to send an amount less than 100% of their
5214 // balance of an IOU with a transfer fee, and such that the
5215 // addition of the transfer fee is still less than their balance
5216 // (buyside)
5217 reinitializeTrustLineBalances();
5218 auto const nftID = mintNFT(minter);
5219 auto const offerID =
5220 createBuyOffer(buyer, minter, nftID, gwXAU(900));
5221 env(token::acceptBuyOffer(minter, offerID));
5222 env.close();
5223
5224 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5225 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5226 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-900));
5227 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-82));
5228 }
5229 {
5230 // Buyer attempts to send an amount less than 100% of their
5231 // balance of an IOU with a transfer fee, and such that the
5232 // addition of the transfer fee is equal than their balance
5233 // (sellside)
5234 reinitializeTrustLineBalances();
5235
5236 // pay them an additional XAU 20 to cover transfer rate
5237 env(pay(gw, buyer, gwXAU(20)));
5238 env.close();
5239
5240 auto const nftID = mintNFT(minter);
5241 auto const offerID =
5242 createSellOffer(minter, nftID, gwXAU(1000));
5243 env(token::acceptSellOffer(buyer, offerID));
5244 env.close();
5245
5246 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5247 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5248 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-1000));
5249 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(0));
5250 }
5251 {
5252 // Buyer attempts to send an amount less than 100% of their
5253 // balance of an IOU with a transfer fee, and such that the
5254 // addition of the transfer fee is equal than their balance
5255 // (buyside)
5256 reinitializeTrustLineBalances();
5257
5258 // pay them an additional XAU 20 to cover transfer rate
5259 env(pay(gw, buyer, gwXAU(20)));
5260 env.close();
5261
5262 auto const nftID = mintNFT(minter);
5263 auto const offerID =
5264 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5265 env(token::acceptBuyOffer(minter, offerID));
5266 env.close();
5267
5268 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5269 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5270 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-1000));
5271 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(0));
5272 }
5273 {
5274 // Gateway attempts to buy NFT with their own IOU - no
5275 // transfer fee is calculated here (sellside)
5276 reinitializeTrustLineBalances();
5277
5278 auto const nftID = mintNFT(minter);
5279 auto const offerID =
5280 createSellOffer(minter, nftID, gwXAU(1000));
5281 TER const sellTER = tesSUCCESS;
5282 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5283 env.close();
5284
5285 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5286 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-1000));
5287 }
5288 {
5289 // Gateway attempts to buy NFT with their own IOU - no
5290 // transfer fee is calculated here (buyside)
5291 reinitializeTrustLineBalances();
5292
5293 auto const nftID = mintNFT(minter);
5294 TER const offerTER = tesSUCCESS;
5295 auto const offerID =
5296 createBuyOffer(gw, minter, nftID, gwXAU(1000), {offerTER});
5297 TER const sellTER = tesSUCCESS;
5298 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5299 env.close();
5300
5301 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5302 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-1000));
5303 }
5304 {
5305 // Gateway attempts to buy NFT with their own IOU for more
5306 // than minter trusts (sellside)
5307 reinitializeTrustLineBalances();
5308 auto const nftID = mintNFT(minter);
5309 auto const offerID =
5310 createSellOffer(minter, nftID, gwXAU(5000));
5311 TER const sellTER = tesSUCCESS;
5312 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5313 env.close();
5314
5315 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5316 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-5000));
5317 }
5318 {
5319 // Gateway attempts to buy NFT with their own IOU for more
5320 // than minter trusts (buyside)
5321 reinitializeTrustLineBalances();
5322
5323 auto const nftID = mintNFT(minter);
5324 TER const offerTER = tesSUCCESS;
5325 auto const offerID =
5326 createBuyOffer(gw, minter, nftID, gwXAU(5000), {offerTER});
5327 TER const sellTER = tesSUCCESS;
5328 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5329 env.close();
5330
5331 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5332 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-5000));
5333 }
5334 {
5335 // Gateway is the NFT minter and attempts to sell NFT for an
5336 // amount that would be greater than a balance if there were a
5337 // transfer fee calculated in this transaction. (sellside)
5338 reinitializeTrustLineBalances();
5339 auto const nftID = mintNFT(gw);
5340 auto const offerID = createSellOffer(gw, nftID, gwXAU(1000));
5341 env(token::acceptSellOffer(buyer, offerID));
5342 env.close();
5343
5344 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5345 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(0));
5346 }
5347 {
5348 // Gateway is the NFT minter and attempts to sell NFT for an
5349 // amount that would be greater than a balance if there were a
5350 // transfer fee calculated in this transaction. (buyside)
5351 reinitializeTrustLineBalances();
5352
5353 auto const nftID = mintNFT(gw);
5354 auto const offerID =
5355 createBuyOffer(buyer, gw, nftID, gwXAU(1000));
5356 env(token::acceptBuyOffer(gw, offerID));
5357 env.close();
5358
5359 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5360 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(0));
5361 }
5362 {
5363 // Gateway is the NFT minter and attempts to sell NFT for an
5364 // amount that is greater than a balance before transfer fees.
5365 // (sellside)
5366 reinitializeTrustLineBalances();
5367 auto const nftID = mintNFT(gw);
5368 auto const offerID = createSellOffer(gw, nftID, gwXAU(2000));
5369 env(token::acceptSellOffer(buyer, offerID),
5370 ter(static_cast<TER>(tecINSUFFICIENT_FUNDS)));
5371 env.close();
5372 expectInitialState();
5373 }
5374 {
5375 // Gateway is the NFT minter and attempts to sell NFT for an
5376 // amount that is greater than a balance before transfer fees.
5377 // (buyside)
5378 reinitializeTrustLineBalances();
5379 auto const nftID = mintNFT(gw);
5380 auto const offerID =
5381 createBuyOffer(buyer, gw, nftID, gwXAU(2000));
5382 env(token::acceptBuyOffer(gw, offerID),
5383 ter(static_cast<TER>(tecINSUFFICIENT_FUNDS)));
5384 env.close();
5385 expectInitialState();
5386 }
5387 {
5388 // Minter attempts to sell the token for XPB 10, which they
5389 // have no trust line for and buyer has none of (sellside).
5390 reinitializeTrustLineBalances();
5391 auto const nftID = mintNFT(minter);
5392 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5393 env(token::acceptSellOffer(buyer, offerID),
5394 ter(static_cast<TER>(tecINSUFFICIENT_FUNDS)));
5395 env.close();
5396 expectInitialState();
5397 }
5398 {
5399 // Minter attempts to sell the token for XPB 10, which they
5400 // have no trust line for and buyer has none of (buyside).
5401 reinitializeTrustLineBalances();
5402 auto const nftID = mintNFT(minter);
5403 auto const offerID = createBuyOffer(
5404 buyer,
5405 minter,
5406 nftID,
5407 gwXPB(10),
5408 {static_cast<TER>(tecUNFUNDED_OFFER)});
5409 env(token::acceptBuyOffer(minter, offerID),
5410 ter(static_cast<TER>(tecOBJECT_NOT_FOUND)));
5411 env.close();
5412 expectInitialState();
5413 }
5414 {
5415 // Minter attempts to sell the token for XPB 10 and the buyer
5416 // has it but the minter has no trust line. Trust line is
5417 // created as a result of the tx (sellside).
5418 reinitializeTrustLineBalances();
5419 env(pay(gw, buyer, gwXPB(100)));
5420 env.close();
5421
5422 auto const nftID = mintNFT(minter);
5423 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5424 env(token::acceptSellOffer(buyer, offerID));
5425 env.close();
5426
5427 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5428 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5429 BEAST_EXPECT(env.balance(gw, minter["XPB"]) == gwXPB(-10));
5430 BEAST_EXPECT(env.balance(gw, buyer["XPB"]) == gwXPB(-89.8));
5431 }
5432 {
5433 // Minter attempts to sell the token for XPB 10 and the buyer
5434 // has it but the minter has no trust line. Trust line is
5435 // created as a result of the tx (buyside).
5436 reinitializeTrustLineBalances();
5437 env(pay(gw, buyer, gwXPB(100)));
5438 env.close();
5439
5440 auto const nftID = mintNFT(minter);
5441 auto const offerID =
5442 createBuyOffer(buyer, minter, nftID, gwXPB(10));
5443 env(token::acceptBuyOffer(minter, offerID));
5444 env.close();
5445
5446 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5447 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5448 BEAST_EXPECT(env.balance(gw, minter["XPB"]) == gwXPB(-10));
5449 BEAST_EXPECT(env.balance(gw, buyer["XPB"]) == gwXPB(-89.8));
5450 }
5451 {
5452 // There is a transfer fee on the NFT and buyer has exact
5453 // amount (sellside)
5454 reinitializeTrustLineBalances();
5455
5456 // secondarySeller has to sell it because transfer fees only
5457 // happen on secondary sales
5458 auto const nftID = mintNFT(minter, 3000); // 3%
5459 auto const primaryOfferID =
5460 createSellOffer(minter, nftID, XRP(0));
5461 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5462 env.close();
5463
5464 // now we can do a secondary sale
5465 auto const offerID =
5466 createSellOffer(secondarySeller, nftID, gwXAU(1000));
5467 TER const sellTER = tecINSUFFICIENT_FUNDS;
5468 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5469 env.close();
5470
5471 expectInitialState();
5472 }
5473 {
5474 // There is a transfer fee on the NFT and buyer has exact
5475 // amount (buyside)
5476 reinitializeTrustLineBalances();
5477
5478 // secondarySeller has to sell it because transfer fees only
5479 // happen on secondary sales
5480 auto const nftID = mintNFT(minter, 3000); // 3%
5481 auto const primaryOfferID =
5482 createSellOffer(minter, nftID, XRP(0));
5483 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5484 env.close();
5485
5486 // now we can do a secondary sale
5487 auto const offerID =
5488 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(1000));
5489 TER const sellTER = tecINSUFFICIENT_FUNDS;
5490 env(token::acceptBuyOffer(secondarySeller, offerID),
5491 ter(sellTER));
5492 env.close();
5493
5494 expectInitialState();
5495 }
5496 {
5497 // There is a transfer fee on the NFT and buyer has enough
5498 // (sellside)
5499 reinitializeTrustLineBalances();
5500
5501 // secondarySeller has to sell it because transfer fees only
5502 // happen on secondary sales
5503 auto const nftID = mintNFT(minter, 3000); // 3%
5504 auto const primaryOfferID =
5505 createSellOffer(minter, nftID, XRP(0));
5506 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5507 env.close();
5508
5509 // now we can do a secondary sale
5510 auto const offerID =
5511 createSellOffer(secondarySeller, nftID, gwXAU(900));
5512 env(token::acceptSellOffer(buyer, offerID));
5513 env.close();
5514
5515 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5516 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5517 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5518 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-27));
5519 BEAST_EXPECT(
5520 env.balance(gw, secondarySeller["XAU"]) == gwXAU(-873));
5521 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-82));
5522 }
5523 {
5524 // There is a transfer fee on the NFT and buyer has enough
5525 // (buyside)
5526 reinitializeTrustLineBalances();
5527
5528 // secondarySeller has to sell it because transfer fees only
5529 // happen on secondary sales
5530 auto const nftID = mintNFT(minter, 3000); // 3%
5531 auto const primaryOfferID =
5532 createSellOffer(minter, nftID, XRP(0));
5533 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5534 env.close();
5535
5536 // now we can do a secondary sale
5537 auto const offerID =
5538 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(900));
5539 env(token::acceptBuyOffer(secondarySeller, offerID));
5540 env.close();
5541
5542 // receives 3% of 900 - 27
5543 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5544 // receives 97% of 900 - 873
5545 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5546 // pays 900 plus 2% transfer fee on XAU - 918
5547 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5548 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-27));
5549 BEAST_EXPECT(
5550 env.balance(gw, secondarySeller["XAU"]) == gwXAU(-873));
5551 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-82));
5552 }
5553 {
5554 // There is a broker fee on the NFT. XAU transfer fee is only
5555 // calculated from the buyer's output, not deducted from
5556 // broker fee.
5557 //
5558 // For a payment of 500 with a 2% IOU transfee fee and 100
5559 // broker fee:
5560 //
5561 // A) Total sale amount + IOU transfer fee is paid by buyer
5562 // (Buyer pays (1.02 * 500) = 510)
5563 // B) GW receives the additional IOU transfer fee
5564 // (GW receives 10 from buyer calculated above)
5565 // C) Broker receives broker fee (no IOU transfer fee)
5566 // (Broker receives 100 from buyer)
5567 // D) Seller receives balance (no IOU transfer fee)
5568 // (Seller receives (510 - 10 - 100) = 400)
5569 reinitializeTrustLineBalances();
5570
5571 auto const nftID = mintNFT(minter);
5572 auto const sellOffer =
5573 createSellOffer(minter, nftID, gwXAU(300));
5574 auto const buyOffer =
5575 createBuyOffer(buyer, minter, nftID, gwXAU(500));
5576 env(token::brokerOffers(broker, buyOffer, sellOffer),
5577 token::brokerFee(gwXAU(100)));
5578 env.close();
5579
5580 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(400));
5581 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5582 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5583 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-400));
5584 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-490));
5585 BEAST_EXPECT(env.balance(gw, broker["XAU"]) == gwXAU(-5100));
5586 }
5587 {
5588 // There is broker and transfer fee on the NFT
5589 //
5590 // For a payment of 500 with a 2% IOU transfer fee, 3% NFT
5591 // transfer fee, and 100 broker fee:
5592 //
5593 // A) Total sale amount + IOU transfer fee is paid by buyer
5594 // (Buyer pays (1.02 * 500) = 510)
5595 // B) GW receives the additional IOU transfer fee
5596 // (GW receives 10 from buyer calculated above)
5597 // C) Broker receives broker fee (no IOU transfer fee)
5598 // (Broker receives 100 from buyer)
5599 // D) Minter receives transfer fee (no IOU transfer fee)
5600 // (Minter receives 0.03 * (510 - 10 - 100) = 12)
5601 // E) Seller receives balance (no IOU transfer fee)
5602 // (Seller receives (510 - 10 - 100 - 12) = 388)
5603 reinitializeTrustLineBalances();
5604
5605 // secondarySeller has to sell it because transfer fees only
5606 // happen on secondary sales
5607 auto const nftID = mintNFT(minter, 3000); // 3%
5608 auto const primaryOfferID =
5609 createSellOffer(minter, nftID, XRP(0));
5610 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5611 env.close();
5612
5613 // now we can do a secondary sale
5614 auto const sellOffer =
5615 createSellOffer(secondarySeller, nftID, gwXAU(300));
5616 auto const buyOffer =
5617 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(500));
5618 env(token::brokerOffers(broker, buyOffer, sellOffer),
5619 token::brokerFee(gwXAU(100)));
5620 env.close();
5621
5622 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(12));
5623 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5624 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(388));
5625 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5626 BEAST_EXPECT(env.balance(gw, minter["XAU"]) == gwXAU(-12));
5627 BEAST_EXPECT(env.balance(gw, buyer["XAU"]) == gwXAU(-490));
5628 BEAST_EXPECT(
5629 env.balance(gw, secondarySeller["XAU"]) == gwXAU(-388));
5630 BEAST_EXPECT(env.balance(gw, broker["XAU"]) == gwXAU(-5100));
5631 }
5632 }
5633 }
5634
5635 void
5637 {
5638 // There was a bug that if an account had...
5639 //
5640 // 1. An NFToken, and
5641 // 2. An offer on the ledger to buy that same token, and
5642 // 3. Also an offer of the ledger to sell that same token,
5643 //
5644 // Then someone could broker the two offers. This would result in
5645 // the NFToken being bought and returned to the original owner and
5646 // the broker pocketing the profit.
5647 //
5648 testcase("Brokered sale to self");
5649
5650 using namespace test::jtx;
5651
5652 Account const alice{"alice"};
5653 Account const bob{"bob"};
5654 Account const broker{"broker"};
5655
5656 Env env{*this, features};
5657 auto const baseFee = env.current()->fees().base;
5658 env.fund(XRP(10000), alice, bob, broker);
5659 env.close();
5660
5661 // For this scenario to occur we need the following steps:
5662 //
5663 // 1. alice mints NFT.
5664 // 2. bob creates a buy offer for it for 5 XRP.
5665 // 3. alice decides to gift the NFT to bob for 0.
5666 // creating a sell offer (hopefully using a destination too)
5667 // 4. Bob accepts the sell offer, because it is better than
5668 // paying 5 XRP.
5669 // 5. At this point, bob has the NFT and still has their buy
5670 // offer from when they did not have the NFT! This is because
5671 // the order book is not cleared when an NFT changes hands.
5672 // 6. Now that Bob owns the NFT, he cannot create new buy offers.
5673 // However he still has one left over from when he did not own
5674 // it. He can create new sell offers and does.
5675 // 7. Now that bob has both a buy and a sell offer for the same NFT,
5676 // a broker can sell the NFT that bob owns to bob and pocket the
5677 // difference.
5678 uint256 const nftId{token::getNextID(env, alice, 0u, tfTransferable)};
5679 env(token::mint(alice, 0u), txflags(tfTransferable));
5680 env.close();
5681
5682 // Bob creates a buy offer for 5 XRP. Alice creates a sell offer
5683 // for 0 XRP.
5684 uint256 const bobBuyOfferIndex =
5685 keylet::nftoffer(bob, env.seq(bob)).key;
5686 env(token::createOffer(bob, nftId, XRP(5)), token::owner(alice));
5687
5688 uint256 const aliceSellOfferIndex =
5689 keylet::nftoffer(alice, env.seq(alice)).key;
5690 env(token::createOffer(alice, nftId, XRP(0)),
5691 token::destination(bob),
5692 txflags(tfSellNFToken));
5693 env.close();
5694
5695 // bob accepts alice's offer but forgets to remove the old buy offer.
5696 env(token::acceptSellOffer(bob, aliceSellOfferIndex));
5697 env.close();
5698
5699 // Note that bob still has a buy offer on the books.
5700 BEAST_EXPECT(env.le(keylet::nftoffer(bobBuyOfferIndex)));
5701
5702 // Bob creates a sell offer for the gift NFT from alice.
5703 uint256 const bobSellOfferIndex =
5704 keylet::nftoffer(bob, env.seq(bob)).key;
5705 env(token::createOffer(bob, nftId, XRP(4)), txflags(tfSellNFToken));
5706 env.close();
5707
5708 // bob now has a buy offer and a sell offer on the books. A broker
5709 // spots this and swoops in to make a profit.
5710 BEAST_EXPECT(nftCount(env, bob) == 1);
5711 auto const bobsPriorBalance = env.balance(bob);
5712 auto const brokersPriorBalance = env.balance(broker);
5713 env(token::brokerOffers(broker, bobBuyOfferIndex, bobSellOfferIndex),
5714 token::brokerFee(XRP(1)),
5716 env.close();
5717
5718 // A tec result was returned, so no state should change other
5719 // than the broker burning their transaction fee.
5720 BEAST_EXPECT(nftCount(env, bob) == 1);
5721 BEAST_EXPECT(env.balance(bob) == bobsPriorBalance);
5722 BEAST_EXPECT(env.balance(broker) == brokersPriorBalance - baseFee);
5723 }
5724
5725 void
5727 {
5728 using namespace test::jtx;
5729
5730 testcase("NFTokenRemint");
5731
5732 // Returns the current ledger sequence
5733 auto openLedgerSeq = [](Env& env) { return env.current()->seq(); };
5734
5735 // Close the ledger until the ledger sequence is large enough to delete
5736 // the account (no longer within <Sequence + 256>)
5737 // This is enforced by the featureDeletableAccounts amendment
5738 auto incLgrSeqForAcctDel = [&](Env& env, Account const& acct) {
5739 int const delta = [&]() -> int {
5740 if (env.seq(acct) + 255 > openLedgerSeq(env))
5741 return env.seq(acct) - openLedgerSeq(env) + 255;
5742 return 0;
5743 }();
5744 BEAST_EXPECT(delta >= 0);
5745 for (int i = 0; i < delta; ++i)
5746 env.close();
5747 BEAST_EXPECT(openLedgerSeq(env) == env.seq(acct) + 255);
5748 };
5749
5750 // Close the ledger until the ledger sequence is no longer
5751 // within <FirstNFTokenSequence + MintedNFTokens + 256>.
5752 auto incLgrSeqForFixNftRemint = [&](Env& env, Account const& acct) {
5753 int delta = 0;
5754 auto const deletableLgrSeq =
5755 (*env.le(acct))[~sfFirstNFTokenSequence].value_or(0) +
5756 (*env.le(acct))[sfMintedNFTokens] + 255;
5757
5758 if (deletableLgrSeq > openLedgerSeq(env))
5759 delta = deletableLgrSeq - openLedgerSeq(env);
5760
5761 BEAST_EXPECT(delta >= 0);
5762 for (int i = 0; i < delta; ++i)
5763 env.close();
5764 BEAST_EXPECT(openLedgerSeq(env) == deletableLgrSeq);
5765 };
5766
5767 // We check if NFTokenIDs can be duplicated by
5768 // re-creation of an account
5769 {
5770 Env env{*this, features};
5771 Account const alice("alice");
5772 Account const becky("becky");
5773
5774 env.fund(XRP(10000), alice, becky);
5775 env.close();
5776
5777 // alice mint and burn a NFT
5778 uint256 const prevNFTokenID = token::getNextID(env, alice, 0u);
5779 env(token::mint(alice));
5780 env.close();
5781 env(token::burn(alice, prevNFTokenID));
5782 env.close();
5783
5784 // alice has minted 1 NFToken
5785 BEAST_EXPECT((*env.le(alice))[sfMintedNFTokens] == 1);
5786
5787 // Close enough ledgers to delete alice's account
5788 incLgrSeqForAcctDel(env, alice);
5789
5790 // alice's account is deleted
5791 Keylet const aliceAcctKey{keylet::account(alice.id())};
5792 auto const acctDelFee{drops(env.current()->fees().increment)};
5793 env(acctdelete(alice, becky), fee(acctDelFee));
5794 env.close();
5795
5796 // alice's account root is gone from the most recently
5797 // closed ledger and the current ledger.
5798 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
5799 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
5800
5801 // Fund alice to re-create her account
5802 env.fund(XRP(10000), alice);
5803 env.close();
5804
5805 // alice's account now exists and has minted 0 NFTokens
5806 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
5807 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
5808 BEAST_EXPECT((*env.le(alice))[sfMintedNFTokens] == 0);
5809
5810 // alice mints a NFT with same params as prevNFTokenID
5811 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
5812 env(token::mint(alice));
5813 env.close();
5814
5815 // burn the NFT to make sure alice owns remintNFTokenID
5816 env(token::burn(alice, remintNFTokenID));
5817 env.close();
5818
5819 // Check that two NFTs don't have the same ID
5820 BEAST_EXPECT(remintNFTokenID != prevNFTokenID);
5821 }
5822
5823 // Test if the issuer account can be deleted after an authorized
5824 // minter mints and burns a batch of NFTokens.
5825 {
5826 Env env{*this, features};
5827 Account const alice("alice");
5828 Account const becky("becky");
5829 Account const minter{"minter"};
5830
5831 env.fund(XRP(10000), alice, becky, minter);
5832 env.close();
5833
5834 // alice sets minter as her authorized minter
5835 env(token::setMinter(alice, minter));
5836 env.close();
5837
5838 // minter mints 500 NFTs for alice
5839 std::vector<uint256> nftIDs;
5840 nftIDs.reserve(500);
5841 for (int i = 0; i < 500; i++)
5842 {
5843 uint256 const nftokenID = token::getNextID(env, alice, 0u);
5844 nftIDs.push_back(nftokenID);
5845 env(token::mint(minter), token::issuer(alice));
5846 }
5847 env.close();
5848
5849 // minter burns 500 NFTs
5850 for (auto const nftokenID : nftIDs)
5851 {
5852 env(token::burn(minter, nftokenID));
5853 }
5854 env.close();
5855
5856 // Increment ledger sequence to the number that is
5857 // enforced by the featureDeletableAccounts amendment
5858 incLgrSeqForAcctDel(env, alice);
5859
5860 // Verify that alice's account root is present.
5861 Keylet const aliceAcctKey{keylet::account(alice.id())};
5862 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
5863 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
5864
5865 auto const acctDelFee{drops(env.current()->fees().increment)};
5866
5867 // alice tries to delete her account, but is unsuccessful.
5868 // Due to authorized minting, alice's account sequence does not
5869 // advance while minter mints NFTokens for her.
5870 // The new account deletion retriction <FirstNFTokenSequence +
5871 // MintedNFTokens + 256> enabled by this amendment will enforce
5872 // alice to wait for more ledgers to close before she can
5873 // delete her account, to prevent duplicate NFTokenIDs
5874 env(acctdelete(alice, becky), fee(acctDelFee), ter(tecTOO_SOON));
5875 env.close();
5876
5877 // alice's account is still present
5878 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
5879
5880 // Close more ledgers until it is no longer within
5881 // <FirstNFTokenSequence + MintedNFTokens + 256>
5882 // to be able to delete alice's account
5883 incLgrSeqForFixNftRemint(env, alice);
5884
5885 // alice's account is deleted
5886 env(acctdelete(alice, becky), fee(acctDelFee));
5887 env.close();
5888
5889 // alice's account root is gone from the most recently
5890 // closed ledger and the current ledger.
5891 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
5892 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
5893
5894 // Fund alice to re-create her account
5895 env.fund(XRP(10000), alice);
5896 env.close();
5897
5898 // alice's account now exists and has minted 0 NFTokens
5899 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
5900 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
5901 BEAST_EXPECT((*env.le(alice))[sfMintedNFTokens] == 0);
5902
5903 // alice mints a NFT with same params as the first one before
5904 // the account delete.
5905 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
5906 env(token::mint(alice));
5907 env.close();
5908
5909 // burn the NFT to make sure alice owns remintNFTokenID
5910 env(token::burn(alice, remintNFTokenID));
5911 env.close();
5912
5913 // The new NFT minted will not have the same ID
5914 // as any of the NFTs authorized minter minted
5915 BEAST_EXPECT(
5916 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
5917 nftIDs.end());
5918 }
5919
5920 // When an account mints and burns a batch of NFTokens using tickets,
5921 // see if the account can be deleted.
5922 {
5923 Env env{*this, features};
5924
5925 Account const alice{"alice"};
5926 Account const becky{"becky"};
5927 env.fund(XRP(10000), alice, becky);
5928 env.close();
5929
5930 // alice grab enough tickets for all of the following
5931 // transactions. Note that once the tickets are acquired alice's
5932 // account sequence number should not advance.
5933 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
5934 env(ticket::create(alice, 100));
5935 env.close();
5936
5937 BEAST_EXPECT(ticketCount(env, alice) == 100);
5938 BEAST_EXPECT(ownerCount(env, alice) == 100);
5939
5940 // alice mints 50 NFTs using tickets
5941 std::vector<uint256> nftIDs;
5942 nftIDs.reserve(50);
5943 for (int i = 0; i < 50; i++)
5944 {
5945 nftIDs.push_back(token::getNextID(env, alice, 0u));
5946 env(token::mint(alice, 0u), ticket::use(aliceTicketSeq++));
5947 env.close();
5948 }
5949
5950 // alice burns 50 NFTs using tickets
5951 for (auto const nftokenID : nftIDs)
5952 {
5953 env(token::burn(alice, nftokenID),
5954 ticket::use(aliceTicketSeq++));
5955 }
5956 env.close();
5957
5958 BEAST_EXPECT(ticketCount(env, alice) == 0);
5959
5960 // Increment ledger sequence to the number that is
5961 // enforced by the featureDeletableAccounts amendment
5962 incLgrSeqForAcctDel(env, alice);
5963
5964 // Verify that alice's account root is present.
5965 Keylet const aliceAcctKey{keylet::account(alice.id())};
5966 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
5967 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
5968
5969 auto const acctDelFee{drops(env.current()->fees().increment)};
5970
5971 // alice tries to delete her account, but is unsuccessful.
5972 // Due to authorized minting, alice's account sequence does not
5973 // advance while minter mints NFTokens for her using tickets.
5974 // The new account deletion retriction <FirstNFTokenSequence +
5975 // MintedNFTokens + 256> enabled by this amendment will enforce
5976 // alice to wait for more ledgers to close before she can
5977 // delete her account, to prevent duplicate NFTokenIDs
5978 env(acctdelete(alice, becky), fee(acctDelFee), ter(tecTOO_SOON));
5979 env.close();
5980
5981 // alice's account is still present
5982 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
5983
5984 // Close more ledgers until it is no longer within
5985 // <FirstNFTokenSequence + MintedNFTokens + 256>
5986 // to be able to delete alice's account
5987 incLgrSeqForFixNftRemint(env, alice);
5988
5989 // alice's account is deleted
5990 env(acctdelete(alice, becky), fee(acctDelFee));
5991 env.close();
5992
5993 // alice's account root is gone from the most recently
5994 // closed ledger and the current ledger.
5995 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
5996 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
5997
5998 // Fund alice to re-create her account
5999 env.fund(XRP(10000), alice);
6000 env.close();
6001
6002 // alice's account now exists and has minted 0 NFTokens
6003 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6004 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6005 BEAST_EXPECT((*env.le(alice))[sfMintedNFTokens] == 0);
6006
6007 // alice mints a NFT with same params as the first one before
6008 // the account delete.
6009 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
6010 env(token::mint(alice));
6011 env.close();
6012
6013 // burn the NFT to make sure alice owns remintNFTokenID
6014 env(token::burn(alice, remintNFTokenID));
6015 env.close();
6016
6017 // The new NFT minted will not have the same ID
6018 // as any of the NFTs authorized minter minted using tickets
6019 BEAST_EXPECT(
6020 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6021 nftIDs.end());
6022 }
6023 // When an authorized minter mints and burns a batch of NFTokens using
6024 // tickets, issuer's account needs to wait a longer time before it can
6025 // be deleted.
6026 // After the issuer's account is re-created and mints a NFT, it should
6027 // not have the same NFTokenID as the ones authorized minter minted.
6028 Env env{*this, features};
6029 Account const alice("alice");
6030 Account const becky("becky");
6031 Account const minter{"minter"};
6032
6033 env.fund(XRP(10000), alice, becky, minter);
6034 env.close();
6035
6036 // alice sets minter as her authorized minter
6037 env(token::setMinter(alice, minter));
6038 env.close();
6039
6040 // minter creates 100 tickets
6041 std::uint32_t minterTicketSeq{env.seq(minter) + 1};
6042 env(ticket::create(minter, 100));
6043 env.close();
6044
6045 BEAST_EXPECT(ticketCount(env, minter) == 100);
6046 BEAST_EXPECT(ownerCount(env, minter) == 100);
6047
6048 // minter mints 50 NFTs for alice using tickets
6049 std::vector<uint256> nftIDs;
6050 nftIDs.reserve(50);
6051 for (int i = 0; i < 50; i++)
6052 {
6053 uint256 const nftokenID = token::getNextID(env, alice, 0u);
6054 nftIDs.push_back(nftokenID);
6055 env(token::mint(minter),
6056 token::issuer(alice),
6057 ticket::use(minterTicketSeq++));
6058 }
6059 env.close();
6060
6061 // minter burns 50 NFTs using tickets
6062 for (auto const nftokenID : nftIDs)
6063 {
6064 env(token::burn(minter, nftokenID), ticket::use(minterTicketSeq++));
6065 }
6066 env.close();
6067
6068 BEAST_EXPECT(ticketCount(env, minter) == 0);
6069
6070 // Increment ledger sequence to the number that is
6071 // enforced by the featureDeletableAccounts amendment
6072 incLgrSeqForAcctDel(env, alice);
6073
6074 // Verify that alice's account root is present.
6075 Keylet const aliceAcctKey{keylet::account(alice.id())};
6076 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6077 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6078
6079 // alice tries to delete her account, but is unsuccessful.
6080 // Due to authorized minting, alice's account sequence does not
6081 // advance while minter mints NFTokens for her using tickets.
6082 // The new account deletion retriction <FirstNFTokenSequence +
6083 // MintedNFTokens + 256> enabled by this amendment will enforce
6084 // alice to wait for more ledgers to close before she can delete her
6085 // account, to prevent duplicate NFTokenIDs
6086 auto const acctDelFee{drops(env.current()->fees().increment)};
6087 env(acctdelete(alice, becky), fee(acctDelFee), ter(tecTOO_SOON));
6088 env.close();
6089
6090 // alice's account is still present
6091 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6092
6093 // Close more ledgers until it is no longer within
6094 // <FirstNFTokenSequence + MintedNFTokens + 256>
6095 // to be able to delete alice's account
6096 incLgrSeqForFixNftRemint(env, alice);
6097
6098 // alice's account is deleted
6099 env(acctdelete(alice, becky), fee(acctDelFee));
6100 env.close();
6101
6102 // alice's account root is gone from the most recently
6103 // closed ledger and the current ledger.
6104 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6105 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6106
6107 // Fund alice to re-create her account
6108 env.fund(XRP(10000), alice);
6109 env.close();
6110
6111 // alice's account now exists and has minted 0 NFTokens
6112 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6113 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6114 BEAST_EXPECT((*env.le(alice))[sfMintedNFTokens] == 0);
6115
6116 // The new NFT minted will not have the same ID
6117 // as any of the NFTs authorized minter minted using tickets
6118 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
6119 env(token::mint(alice));
6120 env.close();
6121
6122 // burn the NFT to make sure alice owns remintNFTokenID
6123 env(token::burn(alice, remintNFTokenID));
6124 env.close();
6125
6126 // The new NFT minted will not have the same ID
6127 // as one of NFTs authorized minter minted using tickets
6128 BEAST_EXPECT(
6129 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6130 nftIDs.end());
6131 }
6132
6133 void
6135 {
6136 testcase("NFTokenMint with Create NFTokenOffer");
6137
6138 using namespace test::jtx;
6139
6140 if (!features[featureNFTokenMintOffer])
6141 {
6142 Env env{*this, features};
6143 Account const alice("alice");
6144 Account const buyer("buyer");
6145
6146 env.fund(XRP(10000), alice, buyer);
6147 env.close();
6148
6149 env(token::mint(alice),
6150 token::amount(XRP(10000)),
6151 ter(temDISABLED));
6152 env.close();
6153
6154 env(token::mint(alice),
6155 token::destination("buyer"),
6156 ter(temDISABLED));
6157 env.close();
6158
6159 env(token::mint(alice),
6160 token::expiration(lastClose(env) + 25),
6161 ter(temDISABLED));
6162 env.close();
6163
6164 return;
6165 }
6166
6167 // The remaining tests assume featureNFTokenMintOffer is enabled.
6168 {
6169 Env env{*this, features};
6170 auto const baseFee = env.current()->fees().base;
6171 Account const alice("alice");
6172 Account const buyer{"buyer"};
6173 Account const gw("gw");
6174 Account const issuer("issuer");
6175 Account const minter("minter");
6176 Account const bob("bob");
6177 IOU const gwAUD(gw["AUD"]);
6178
6179 env.fund(XRP(10000), alice, buyer, gw, issuer, minter);
6180 env.close();
6181
6182 {
6183 // Destination field specified but Amount field not specified
6184 env(token::mint(alice),
6185 token::destination(buyer),
6186 ter(temMALFORMED));
6187 env.close();
6188 BEAST_EXPECT(ownerCount(env, alice) == 0);
6189
6190 // Expiration field specified but Amount field not specified
6191 env(token::mint(alice),
6192 token::expiration(lastClose(env) + 25),
6193 ter(temMALFORMED));
6194 env.close();
6195 BEAST_EXPECT(ownerCount(env, buyer) == 0);
6196 }
6197
6198 {
6199 // The destination may not be the account submitting the
6200 // transaction.
6201 env(token::mint(alice),
6202 token::amount(XRP(1000)),
6203 token::destination(alice),
6204 ter(temMALFORMED));
6205 env.close();
6206 BEAST_EXPECT(ownerCount(env, alice) == 0);
6207
6208 // The destination must be an account already established in the
6209 // ledger.
6210 env(token::mint(alice),
6211 token::amount(XRP(1000)),
6212 token::destination(Account("demon")),
6213 ter(tecNO_DST));
6214 env.close();
6215 BEAST_EXPECT(ownerCount(env, alice) == 0);
6216 }
6217
6218 {
6219 // Set a bad expiration.
6220 env(token::mint(alice),
6221 token::amount(XRP(1000)),
6222 token::expiration(0),
6223 ter(temBAD_EXPIRATION));
6224 env.close();
6225 BEAST_EXPECT(ownerCount(env, alice) == 0);
6226
6227 // The new NFTokenOffer may not have passed its expiration time.
6228 env(token::mint(alice),
6229 token::amount(XRP(1000)),
6230 token::expiration(lastClose(env)),
6231 ter(tecEXPIRED));
6232 env.close();
6233 BEAST_EXPECT(ownerCount(env, alice) == 0);
6234 }
6235
6236 {
6237 // Set an invalid amount.
6238 env(token::mint(alice),
6239 token::amount(buyer["USD"](1)),
6240 txflags(tfOnlyXRP),
6241 ter(temBAD_AMOUNT));
6242 env(token::mint(alice),
6243 token::amount(buyer["USD"](0)),
6244 ter(temBAD_AMOUNT));
6245 env.close();
6246 BEAST_EXPECT(ownerCount(env, alice) == 0);
6247
6248 // Issuer (alice) must have a trust line for the offered funds.
6249 env(token::mint(alice),
6250 token::amount(gwAUD(1000)),
6251 txflags(tfTransferable),
6252 token::xferFee(10),
6253 ter(tecNO_LINE));
6254 env.close();
6255 BEAST_EXPECT(ownerCount(env, alice) == 0);
6256
6257 // If the IOU issuer and the NFToken issuer are the same,
6258 // then that issuer does not need a trust line to accept their
6259 // fee.
6260 env(token::mint(gw),
6261 token::amount(gwAUD(1000)),
6262 txflags(tfTransferable),
6263 token::xferFee(10));
6264 env.close();
6265
6266 // Give alice the needed trust line, but freeze it.
6267 env(trust(gw, alice["AUD"](999), tfSetFreeze));
6268 env.close();
6269
6270 // Issuer (alice) must have a trust line for the offered funds
6271 // and the trust line may not be frozen.
6272 env(token::mint(alice),
6273 token::amount(gwAUD(1000)),
6274 txflags(tfTransferable),
6275 token::xferFee(10),
6276 ter(tecFROZEN));
6277 env.close();
6278 BEAST_EXPECT(ownerCount(env, alice) == 0);
6279
6280 // Seller (alice) must have a trust line may not be frozen.
6281 env(token::mint(alice),
6282 token::amount(gwAUD(1000)),
6283 ter(tecFROZEN));
6284 env.close();
6285 BEAST_EXPECT(ownerCount(env, alice) == 0);
6286
6287 // Unfreeze alice's trustline.
6288 env(trust(gw, alice["AUD"](999), tfClearFreeze));
6289 env.close();
6290 }
6291
6292 {
6293 // check reserve
6294 auto const acctReserve = env.current()->fees().reserve;
6295 auto const incReserve = env.current()->fees().increment;
6296
6297 env.fund(acctReserve + incReserve, bob);
6298 env.close();
6299
6300 // doesn't have reserve for 2 objects (NFTokenPage, Offer)
6301 env(token::mint(bob),
6302 token::amount(XRP(0)),
6304 env.close();
6305
6306 // have reserve for NFTokenPage, Offer
6307 env(pay(env.master, bob, incReserve + drops(baseFee)));
6308 env.close();
6309 env(token::mint(bob), token::amount(XRP(0)));
6310 env.close();
6311
6312 // doesn't have reserve for Offer
6313 env(pay(env.master, bob, drops(baseFee)));
6314 env.close();
6315 env(token::mint(bob),
6316 token::amount(XRP(0)),
6318 env.close();
6319
6320 // have reserve for Offer
6321 env(pay(env.master, bob, incReserve + drops(baseFee)));
6322 env.close();
6323 env(token::mint(bob), token::amount(XRP(0)));
6324 env.close();
6325 }
6326
6327 // Amount field specified
6328 BEAST_EXPECT(ownerCount(env, alice) == 0);
6329 env(token::mint(alice), token::amount(XRP(10)));
6330 BEAST_EXPECT(ownerCount(env, alice) == 2);
6331 env.close();
6332
6333 // Amount field and Destination field, Expiration field specified
6334 env(token::mint(alice),
6335 token::amount(XRP(10)),
6336 token::destination(buyer),
6337 token::expiration(lastClose(env) + 25));
6338 env.close();
6339
6340 // With TransferFee field
6341 env(trust(alice, gwAUD(1000)));
6342 env.close();
6343 env(token::mint(alice),
6344 token::amount(gwAUD(1)),
6345 token::destination(buyer),
6346 token::expiration(lastClose(env) + 25),
6347 txflags(tfTransferable),
6348 token::xferFee(10));
6349 env.close();
6350
6351 // Can be canceled by the issuer.
6352 env(token::mint(alice),
6353 token::amount(XRP(10)),
6354 token::destination(buyer),
6355 token::expiration(lastClose(env) + 25));
6356 uint256 const offerAliceSellsToBuyer =
6357 keylet::nftoffer(alice, env.seq(alice)).key;
6358 env(token::cancelOffer(alice, {offerAliceSellsToBuyer}));
6359 env.close();
6360
6361 // Can be canceled by the buyer.
6362 env(token::mint(buyer),
6363 token::amount(XRP(10)),
6364 token::destination(alice),
6365 token::expiration(lastClose(env) + 25));
6366 uint256 const offerBuyerSellsToAlice =
6367 keylet::nftoffer(buyer, env.seq(buyer)).key;
6368 env(token::cancelOffer(alice, {offerBuyerSellsToAlice}));
6369 env.close();
6370
6371 env(token::setMinter(issuer, minter));
6372 env.close();
6373
6374 // Minter will have offer not issuer
6375 BEAST_EXPECT(ownerCount(env, minter) == 0);
6376 BEAST_EXPECT(ownerCount(env, issuer) == 0);
6377 env(token::mint(minter),
6378 token::issuer(issuer),
6379 token::amount(drops(1)));
6380 env.close();
6381 BEAST_EXPECT(ownerCount(env, minter) == 2);
6382 BEAST_EXPECT(ownerCount(env, issuer) == 0);
6383 }
6384
6385 Env env{*this, features};
6386 Account const alice("alice");
6387
6388 env.fund(XRP(1000000), alice);
6389
6390 TER const offerCreateTER = temBAD_AMOUNT;
6391
6392 // Make offers with negative amounts for the NFTs
6393 env(token::mint(alice), token::amount(XRP(-2)), ter(offerCreateTER));
6394 env.close();
6395 }
6396
6397 void
6399 {
6400 // `nftoken_id` is added in the `tx` response for NFTokenMint and
6401 // NFTokenAcceptOffer.
6402 //
6403 // `nftoken_ids` is added in the `tx` response for NFTokenCancelOffer
6404 //
6405 // `offer_id` is added in the `tx` response for NFTokenCreateOffer
6406 //
6407 // The values of these fields are dependent on the NFTokenID/OfferID
6408 // changed in its corresponding transaction. We want to validate each
6409 // transaction to make sure the synethic fields hold the right values.
6410
6411 testcase("Test synthetic fields from JSON response");
6412
6413 using namespace test::jtx;
6414
6415 Account const alice{"alice"};
6416 Account const bob{"bob"};
6417 Account const broker{"broker"};
6418
6419 Env env{*this, features};
6420 env.fund(XRP(10000), alice, bob, broker);
6421 env.close();
6422
6423 // Verify `nftoken_id` value equals to the NFTokenID that was
6424 // changed in the most recent NFTokenMint or NFTokenAcceptOffer
6425 // transaction
6426 auto verifyNFTokenID = [&](uint256 const& actualNftID) {
6427 // Get the hash for the most recent transaction.
6428 std::string const txHash{
6429 env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
6430
6431 env.close();
6432 Json::Value const meta =
6433 env.rpc("tx", txHash)[jss::result][jss::meta];
6434
6435 // Expect nftokens_id field
6436 if (!BEAST_EXPECT(meta.isMember(jss::nftoken_id)))
6437 return;
6438
6439 // Check the value of NFT ID in the meta with the
6440 // actual value
6441 uint256 nftID;
6442 BEAST_EXPECT(nftID.parseHex(meta[jss::nftoken_id].asString()));
6443 BEAST_EXPECT(nftID == actualNftID);
6444 };
6445
6446 // Verify `nftoken_ids` value equals to the NFTokenIDs that were
6447 // changed in the most recent NFTokenCancelOffer transaction
6448 auto verifyNFTokenIDsInCancelOffer =
6449 [&](std::vector<uint256> actualNftIDs) {
6450 // Get the hash for the most recent transaction.
6451 std::string const txHash{
6452 env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
6453
6454 env.close();
6455 Json::Value const meta =
6456 env.rpc("tx", txHash)[jss::result][jss::meta];
6457
6458 // Expect nftokens_ids field and verify the values
6459 if (!BEAST_EXPECT(meta.isMember(jss::nftoken_ids)))
6460 return;
6461
6462 // Convert NFT IDs from Json::Value to uint256
6463 std::vector<uint256> metaIDs;
6465 meta[jss::nftoken_ids].begin(),
6466 meta[jss::nftoken_ids].end(),
6467 std::back_inserter(metaIDs),
6468 [this](Json::Value id) {
6469 uint256 nftID;
6470 BEAST_EXPECT(nftID.parseHex(id.asString()));
6471 return nftID;
6472 });
6473
6474 // Sort both array to prepare for comparison
6475 std::sort(metaIDs.begin(), metaIDs.end());
6476 std::sort(actualNftIDs.begin(), actualNftIDs.end());
6477
6478 // Make sure the expect number of NFTs is correct
6479 BEAST_EXPECT(metaIDs.size() == actualNftIDs.size());
6480
6481 // Check the value of NFT ID in the meta with the
6482 // actual values
6483 for (size_t i = 0; i < metaIDs.size(); ++i)
6484 BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
6485 };
6486
6487 // Verify `offer_id` value equals to the offerID that was
6488 // changed in the most recent NFTokenCreateOffer tx
6489 auto verifyNFTokenOfferID = [&](uint256 const& offerID) {
6490 // Get the hash for the most recent transaction.
6491 std::string const txHash{
6492 env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
6493
6494 env.close();
6495 Json::Value const meta =
6496 env.rpc("tx", txHash)[jss::result][jss::meta];
6497
6498 // Expect offer_id field and verify the value
6499 if (!BEAST_EXPECT(meta.isMember(jss::offer_id)))
6500 return;
6501
6502 uint256 metaOfferID;
6503 BEAST_EXPECT(metaOfferID.parseHex(meta[jss::offer_id].asString()));
6504 BEAST_EXPECT(metaOfferID == offerID);
6505 };
6506
6507 // Check new fields in tx meta when for all NFTtransactions
6508 {
6509 // Alice mints 2 NFTs
6510 // Verify the NFTokenIDs are correct in the NFTokenMint tx meta
6511 uint256 const nftId1{
6512 token::getNextID(env, alice, 0u, tfTransferable)};
6513 env(token::mint(alice, 0u), txflags(tfTransferable));
6514 env.close();
6515 verifyNFTokenID(nftId1);
6516
6517 uint256 const nftId2{
6518 token::getNextID(env, alice, 0u, tfTransferable)};
6519 env(token::mint(alice, 0u), txflags(tfTransferable));
6520 env.close();
6521 verifyNFTokenID(nftId2);
6522
6523 // Alice creates one sell offer for each NFT
6524 // Verify the offer indexes are correct in the NFTokenCreateOffer tx
6525 // meta
6526 uint256 const aliceOfferIndex1 =
6527 keylet::nftoffer(alice, env.seq(alice)).key;
6528 env(token::createOffer(alice, nftId1, drops(1)),
6529 txflags(tfSellNFToken));
6530 env.close();
6531 verifyNFTokenOfferID(aliceOfferIndex1);
6532
6533 uint256 const aliceOfferIndex2 =
6534 keylet::nftoffer(alice, env.seq(alice)).key;
6535 env(token::createOffer(alice, nftId2, drops(1)),
6536 txflags(tfSellNFToken));
6537 env.close();
6538 verifyNFTokenOfferID(aliceOfferIndex2);
6539
6540 // Alice cancels two offers she created
6541 // Verify the NFTokenIDs are correct in the NFTokenCancelOffer tx
6542 // meta
6543 env(token::cancelOffer(
6544 alice, {aliceOfferIndex1, aliceOfferIndex2}));
6545 env.close();
6546 verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
6547
6548 // Bobs creates a buy offer for nftId1
6549 // Verify the offer id is correct in the NFTokenCreateOffer tx meta
6550 auto const bobBuyOfferIndex =
6551 keylet::nftoffer(bob, env.seq(bob)).key;
6552 env(token::createOffer(bob, nftId1, drops(1)), token::owner(alice));
6553 env.close();
6554 verifyNFTokenOfferID(bobBuyOfferIndex);
6555
6556 // Alice accepts bob's buy offer
6557 // Verify the NFTokenID is correct in the NFTokenAcceptOffer tx meta
6558 env(token::acceptBuyOffer(alice, bobBuyOfferIndex));
6559 env.close();
6560 verifyNFTokenID(nftId1);
6561 }
6562
6563 // Check `nftoken_ids` in brokered mode
6564 {
6565 // Alice mints a NFT
6566 uint256 const nftId{
6567 token::getNextID(env, alice, 0u, tfTransferable)};
6568 env(token::mint(alice, 0u), txflags(tfTransferable));
6569 env.close();
6570 verifyNFTokenID(nftId);
6571
6572 // Alice creates sell offer and set broker as destination
6573 uint256 const offerAliceToBroker =
6574 keylet::nftoffer(alice, env.seq(alice)).key;
6575 env(token::createOffer(alice, nftId, drops(1)),
6576 token::destination(broker),
6577 txflags(tfSellNFToken));
6578 env.close();
6579 verifyNFTokenOfferID(offerAliceToBroker);
6580
6581 // Bob creates buy offer
6582 uint256 const offerBobToBroker =
6583 keylet::nftoffer(bob, env.seq(bob)).key;
6584 env(token::createOffer(bob, nftId, drops(1)), token::owner(alice));
6585 env.close();
6586 verifyNFTokenOfferID(offerBobToBroker);
6587
6588 // Check NFTokenID meta for NFTokenAcceptOffer in brokered mode
6589 env(token::brokerOffers(
6590 broker, offerBobToBroker, offerAliceToBroker));
6591 env.close();
6592 verifyNFTokenID(nftId);
6593 }
6594
6595 // Check if there are no duplicate nft id in Cancel transactions where
6596 // multiple offers are cancelled for the same NFT
6597 {
6598 // Alice mints a NFT
6599 uint256 const nftId{
6600 token::getNextID(env, alice, 0u, tfTransferable)};
6601 env(token::mint(alice, 0u), txflags(tfTransferable));
6602 env.close();
6603 verifyNFTokenID(nftId);
6604
6605 // Alice creates 2 sell offers for the same NFT
6606 uint256 const aliceOfferIndex1 =
6607 keylet::nftoffer(alice, env.seq(alice)).key;
6608 env(token::createOffer(alice, nftId, drops(1)),
6609 txflags(tfSellNFToken));
6610 env.close();
6611 verifyNFTokenOfferID(aliceOfferIndex1);
6612
6613 uint256 const aliceOfferIndex2 =
6614 keylet::nftoffer(alice, env.seq(alice)).key;
6615 env(token::createOffer(alice, nftId, drops(1)),
6616 txflags(tfSellNFToken));
6617 env.close();
6618 verifyNFTokenOfferID(aliceOfferIndex2);
6619
6620 // Make sure the metadata only has 1 nft id, since both offers are
6621 // for the same nft
6622 env(token::cancelOffer(
6623 alice, {aliceOfferIndex1, aliceOfferIndex2}));
6624 env.close();
6625 verifyNFTokenIDsInCancelOffer({nftId});
6626 }
6627
6628 if (features[featureNFTokenMintOffer])
6629 {
6630 uint256 const aliceMintWithOfferIndex1 =
6631 keylet::nftoffer(alice, env.seq(alice)).key;
6632 env(token::mint(alice), token::amount(XRP(0)));
6633 env.close();
6634 verifyNFTokenOfferID(aliceMintWithOfferIndex1);
6635 }
6636 }
6637
6638 void
6640 {
6641 testcase("Test buyer reserve when accepting an offer");
6642
6643 using namespace test::jtx;
6644
6645 // Lambda that mints an NFT and then creates a sell offer
6646 auto mintAndCreateSellOffer = [](test::jtx::Env& env,
6647 test::jtx::Account const& acct,
6648 STAmount const amt) -> uint256 {
6649 // acct mints a NFT
6650 uint256 const nftId{
6651 token::getNextID(env, acct, 0u, tfTransferable)};
6652 env(token::mint(acct, 0u), txflags(tfTransferable));
6653 env.close();
6654
6655 // acct makes an sell offer
6656 uint256 const sellOfferIndex =
6657 keylet::nftoffer(acct, env.seq(acct)).key;
6658 env(token::createOffer(acct, nftId, amt), txflags(tfSellNFToken));
6659 env.close();
6660
6661 return sellOfferIndex;
6662 };
6663
6664 // Test the behaviors when the buyer makes an accept offer, both before
6665 // and after enabling the amendment. Exercises the precise number of
6666 // reserve in drops that's required to accept the offer
6667 {
6668 Account const alice{"alice"};
6669 Account const bob{"bob"};
6670
6671 Env env{*this, features};
6672 auto const acctReserve = env.current()->fees().reserve;
6673 auto const incReserve = env.current()->fees().increment;
6674 auto const baseFee = env.current()->fees().base;
6675
6676 env.fund(XRP(10000), alice);
6677 env.close();
6678
6679 // Bob is funded with minimum XRP reserve
6680 env.fund(acctReserve, bob);
6681 env.close();
6682
6683 // alice mints an NFT and create a sell offer for 0 XRP
6684 auto const sellOfferIndex =
6685 mintAndCreateSellOffer(env, alice, XRP(0));
6686
6687 // Bob owns no object
6688 BEAST_EXPECT(ownerCount(env, bob) == 0);
6689
6690 // Without fixNFTokenReserve amendment, when bob accepts an NFT sell
6691 // offer, he can get the NFT free of reserve
6692 if (!features[fixNFTokenReserve])
6693 {
6694 // Bob is able to accept the offer
6695 env(token::acceptSellOffer(bob, sellOfferIndex));
6696 env.close();
6697
6698 // Bob now owns an extra objects
6699 BEAST_EXPECT(ownerCount(env, bob) == 1);
6700
6701 // This is the wrong behavior, since Bob should need at least
6702 // one incremental reserve.
6703 }
6704 // With fixNFTokenReserve, bob can no longer accept the offer unless
6705 // there is enough reserve. A detail to note is that NFTs(sell
6706 // offer) will not allow one to go below the reserve requirement,
6707 // because buyer's balance is computed after the transaction fee is
6708 // deducted. This means that the reserve requirement will be `base
6709 // fee` drops higher than normal.
6710 else
6711 {
6712 // Bob is not able to accept the offer with only the account
6713 // reserve (200,000,000 drops)
6714 env(token::acceptSellOffer(bob, sellOfferIndex),
6716 env.close();
6717
6718 // after prev transaction, Bob owns `200M - base fee` drops due
6719 // to burnt tx fee
6720
6721 BEAST_EXPECT(ownerCount(env, bob) == 0);
6722
6723 // Send bob an increment reserve and base fee (to make up for
6724 // the transaction fee burnt from the prev failed tx) Bob now
6725 // owns 250,000,000 drops
6726 env(pay(env.master, bob, incReserve + drops(baseFee)));
6727 env.close();
6728
6729 // However, this transaction will still fail because the reserve
6730 // requirement is `base fee` drops higher
6731 env(token::acceptSellOffer(bob, sellOfferIndex),
6733 env.close();
6734
6735 // Send bob `base fee * 2` drops
6736 // Bob now owns `250M + base fee` drops
6737 env(pay(env.master, bob, drops(baseFee * 2)));
6738 env.close();
6739
6740 // Bob is now able to accept the offer
6741 env(token::acceptSellOffer(bob, sellOfferIndex));
6742 env.close();
6743
6744 BEAST_EXPECT(ownerCount(env, bob) == 1);
6745 }
6746 }
6747
6748 // Now exercise the scenario when the buyer accepts
6749 // many sell offers
6750 {
6751 Account const alice{"alice"};
6752 Account const bob{"bob"};
6753
6754 Env env{*this, features};
6755 auto const acctReserve = env.current()->fees().reserve;
6756 auto const incReserve = env.current()->fees().increment;
6757
6758 env.fund(XRP(10000), alice);
6759 env.close();
6760
6761 env.fund(acctReserve + XRP(1), bob);
6762 env.close();
6763
6764 if (!features[fixNFTokenReserve])
6765 {
6766 // Bob can accept many NFTs without having a single reserve!
6767 for (size_t i = 0; i < 200; i++)
6768 {
6769 // alice mints an NFT and creates a sell offer for 0 XRP
6770 auto const sellOfferIndex =
6771 mintAndCreateSellOffer(env, alice, XRP(0));
6772
6773 // Bob is able to accept the offer
6774 env(token::acceptSellOffer(bob, sellOfferIndex));
6775 env.close();
6776 }
6777 }
6778 else
6779 {
6780 // alice mints the first NFT and creates a sell offer for 0 XRP
6781 auto const sellOfferIndex1 =
6782 mintAndCreateSellOffer(env, alice, XRP(0));
6783
6784 // Bob cannot accept this offer because he doesn't have the
6785 // reserve for the NFT
6786 env(token::acceptSellOffer(bob, sellOfferIndex1),
6788 env.close();
6789
6790 // Give bob enough reserve
6791 env(pay(env.master, bob, drops(incReserve)));
6792 env.close();
6793
6794 BEAST_EXPECT(ownerCount(env, bob) == 0);
6795
6796 // Bob now owns his first NFT
6797 env(token::acceptSellOffer(bob, sellOfferIndex1));
6798 env.close();
6799
6800 BEAST_EXPECT(ownerCount(env, bob) == 1);
6801
6802 // alice now mints 31 more NFTs and creates an offer for each
6803 // NFT, then sells to bob
6804 for (size_t i = 0; i < 31; i++)
6805 {
6806 // alice mints an NFT and creates a sell offer for 0 XRP
6807 auto const sellOfferIndex =
6808 mintAndCreateSellOffer(env, alice, XRP(0));
6809
6810 // Bob can accept the offer because the new NFT is stored in
6811 // an existing NFTokenPage so no new reserve is requried
6812 env(token::acceptSellOffer(bob, sellOfferIndex));
6813 env.close();
6814 }
6815
6816 BEAST_EXPECT(ownerCount(env, bob) == 1);
6817
6818 // alice now mints the 33rd NFT and creates an sell offer for 0
6819 // XRP
6820 auto const sellOfferIndex33 =
6821 mintAndCreateSellOffer(env, alice, XRP(0));
6822
6823 // Bob fails to accept this NFT because he does not have enough
6824 // reserve for a new NFTokenPage
6825 env(token::acceptSellOffer(bob, sellOfferIndex33),
6827 env.close();
6828
6829 // Send bob incremental reserve
6830 env(pay(env.master, bob, drops(incReserve)));
6831 env.close();
6832
6833 // Bob now has enough reserve to accept the offer and now
6834 // owns one more NFTokenPage
6835 env(token::acceptSellOffer(bob, sellOfferIndex33));
6836 env.close();
6837
6838 BEAST_EXPECT(ownerCount(env, bob) == 2);
6839 }
6840 }
6841
6842 // Test the behavior when the seller accepts a buy offer.
6843 // The behavior should not change regardless whether fixNFTokenReserve
6844 // is enabled or not, since the ledger is able to guard against
6845 // free NFTokenPages when buy offer is accepted. This is merely an
6846 // additional test to exercise existing offer behavior.
6847 {
6848 Account const alice{"alice"};
6849 Account const bob{"bob"};
6850
6851 Env env{*this, features};
6852 auto const acctReserve = env.current()->fees().reserve;
6853 auto const incReserve = env.current()->fees().increment;
6854 auto const baseFee = env.current()->fees().base;
6855
6856 env.fund(XRP(10000), alice);
6857 env.close();
6858
6859 // Bob is funded with account reserve + increment reserve + 1 XRP
6860 // increment reserve is for the buy offer, and 1 XRP is for offer
6861 // price
6862 env.fund(acctReserve + incReserve + XRP(1), bob);
6863 env.close();
6864
6865 // Alice mints a NFT
6866 uint256 const nftId{
6867 token::getNextID(env, alice, 0u, tfTransferable)};
6868 env(token::mint(alice, 0u), txflags(tfTransferable));
6869 env.close();
6870
6871 // Bob makes a buy offer for 1 XRP
6872 auto const buyOfferIndex = keylet::nftoffer(bob, env.seq(bob)).key;
6873 env(token::createOffer(bob, nftId, XRP(1)), token::owner(alice));
6874 env.close();
6875
6876 // accepting the buy offer fails because bob's balance is `base fee`
6877 // drops lower than the required amount, since the previous tx burnt
6878 // drops for tx fee.
6879 env(token::acceptBuyOffer(alice, buyOfferIndex),
6881 env.close();
6882
6883 // send Bob `base fee` drops
6884 env(pay(env.master, bob, drops(baseFee)));
6885 env.close();
6886
6887 // Now bob can buy the offer
6888 env(token::acceptBuyOffer(alice, buyOfferIndex));
6889 env.close();
6890 }
6891
6892 // Test the reserve behavior in brokered mode.
6893 // The behavior should not change regardless whether fixNFTokenReserve
6894 // is enabled or not, since the ledger is able to guard against
6895 // free NFTokenPages in brokered mode. This is merely an
6896 // additional test to exercise existing offer behavior.
6897 {
6898 Account const alice{"alice"};
6899 Account const bob{"bob"};
6900 Account const broker{"broker"};
6901
6902 Env env{*this, features};
6903 auto const acctReserve = env.current()->fees().reserve;
6904 auto const incReserve = env.current()->fees().increment;
6905 auto const baseFee = env.current()->fees().base;
6906
6907 env.fund(XRP(10000), alice, broker);
6908 env.close();
6909
6910 // Bob is funded with account reserve + incr reserve + 1 XRP(offer
6911 // price)
6912 env.fund(acctReserve + incReserve + XRP(1), bob);
6913 env.close();
6914
6915 // Alice mints a NFT
6916 uint256 const nftId{
6917 token::getNextID(env, alice, 0u, tfTransferable)};
6918 env(token::mint(alice, 0u), txflags(tfTransferable));
6919 env.close();
6920
6921 // Alice creates sell offer and set broker as destination
6922 uint256 const offerAliceToBroker =
6923 keylet::nftoffer(alice, env.seq(alice)).key;
6924 env(token::createOffer(alice, nftId, XRP(1)),
6925 token::destination(broker),
6926 txflags(tfSellNFToken));
6927 env.close();
6928
6929 // Bob creates buy offer
6930 uint256 const offerBobToBroker =
6931 keylet::nftoffer(bob, env.seq(bob)).key;
6932 env(token::createOffer(bob, nftId, XRP(1)), token::owner(alice));
6933 env.close();
6934
6935 // broker offers.
6936 // Returns insufficient funds, because bob burnt tx fee when he
6937 // created his buy offer, which makes his spendable balance to be
6938 // less than the required amount.
6939 env(token::brokerOffers(
6940 broker, offerBobToBroker, offerAliceToBroker),
6942 env.close();
6943
6944 // send Bob `base fee` drops
6945 env(pay(env.master, bob, drops(baseFee)));
6946 env.close();
6947
6948 // broker offers.
6949 env(token::brokerOffers(
6950 broker, offerBobToBroker, offerAliceToBroker));
6951 env.close();
6952 }
6953 }
6954
6955 void
6957 {
6958 testcase("Test fix unasked for auto-trustline.");
6959
6960 using namespace test::jtx;
6961
6962 Account const issuer{"issuer"};
6963 Account const becky{"becky"};
6964 Account const cheri{"cheri"};
6965 Account const gw("gw");
6966 IOU const gwAUD(gw["AUD"]);
6967
6968 // This test case covers issue...
6969 // https://github.com/XRPLF/rippled/issues/4925
6970 //
6971 // For an NFToken with a transfer fee, the issuer must be able to
6972 // accept the transfer fee or else a transfer should fail. If the
6973 // NFToken is transferred for a non-XRP asset, then the issuer must
6974 // have a trustline to that asset to receive the fee.
6975 //
6976 // This test looks at a situation where issuer would get a trustline
6977 // for the fee without the issuer's consent. Here are the steps:
6978 // 1. Issuer has a trustline (i.e., USD)
6979 // 2. Issuer mints NFToken with transfer fee.
6980 // 3. Becky acquires the NFToken, paying with XRP.
6981 // 4. Becky creates offer to sell NFToken for USD(100).
6982 // 5. Issuer deletes trustline for USD.
6983 // 6. Carol buys NFToken from Becky for USD(100).
6984 // 7. The transfer fee from Carol's purchase re-establishes issuer's
6985 // USD trustline.
6986 //
6987 // The fixEnforceNFTokenTrustline amendment addresses this oversight.
6988 //
6989 // We run this test case both with and without
6990 // fixEnforceNFTokenTrustline enabled so we can see the change
6991 // in behavior.
6992 //
6993 // In both cases we remove the fixRemoveNFTokenAutoTrustLine amendment.
6994 // Otherwise we can't create NFTokens with tfTrustLine enabled.
6995 FeatureBitset const localFeatures =
6996 features - fixRemoveNFTokenAutoTrustLine;
6997 for (FeatureBitset feats :
6998 {localFeatures - fixEnforceNFTokenTrustline,
6999 localFeatures | fixEnforceNFTokenTrustline})
7000 {
7001 Env env{*this, feats};
7002 env.fund(XRP(1000), issuer, becky, cheri, gw);
7003 env.close();
7004
7005 // Set trust lines so becky and cheri can use gw's currency.
7006 env(trust(becky, gwAUD(1000)));
7007 env(trust(cheri, gwAUD(1000)));
7008 env.close();
7009 env(pay(gw, cheri, gwAUD(500)));
7010 env.close();
7011
7012 // issuer creates two NFTs: one with and one without AutoTrustLine.
7013 std::uint16_t xferFee = 5000; // 5%
7014 uint256 const nftAutoTrustID{token::getNextID(
7015 env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)};
7016 env(token::mint(issuer, 0u),
7017 token::xferFee(xferFee),
7018 txflags(tfTransferable | tfTrustLine));
7019 env.close();
7020
7021 uint256 const nftNoAutoTrustID{
7022 token::getNextID(env, issuer, 0u, tfTransferable, xferFee)};
7023 env(token::mint(issuer, 0u),
7024 token::xferFee(xferFee),
7025 txflags(tfTransferable));
7026 env.close();
7027
7028 // becky buys the nfts for 1 drop each.
7029 {
7030 uint256 const beckyBuyOfferIndex1 =
7031 keylet::nftoffer(becky, env.seq(becky)).key;
7032 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
7033 token::owner(issuer));
7034
7035 uint256 const beckyBuyOfferIndex2 =
7036 keylet::nftoffer(becky, env.seq(becky)).key;
7037 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
7038 token::owner(issuer));
7039
7040 env.close();
7041 env(token::acceptBuyOffer(issuer, beckyBuyOfferIndex1));
7042 env(token::acceptBuyOffer(issuer, beckyBuyOfferIndex2));
7043 env.close();
7044 }
7045
7046 // becky creates offers to sell the nfts for AUD.
7047 uint256 const beckyAutoTrustOfferIndex =
7048 keylet::nftoffer(becky, env.seq(becky)).key;
7049 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
7050 txflags(tfSellNFToken));
7051 env.close();
7052
7053 // Creating an offer for the NFToken without tfTrustLine fails
7054 // because issuer does not have a trust line for AUD.
7055 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
7056 txflags(tfSellNFToken),
7057 ter(tecNO_LINE));
7058 env.close();
7059
7060 // issuer creates a trust line. Now the offer create for the
7061 // NFToken without tfTrustLine succeeds.
7062 BEAST_EXPECT(ownerCount(env, issuer) == 0);
7063 env(trust(issuer, gwAUD(1000)));
7064 env.close();
7065 BEAST_EXPECT(ownerCount(env, issuer) == 1);
7066
7067 uint256 const beckyNoAutoTrustOfferIndex =
7068 keylet::nftoffer(becky, env.seq(becky)).key;
7069 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
7070 txflags(tfSellNFToken));
7071 env.close();
7072
7073 // Now that the offers are in place, issuer removes the trustline.
7074 BEAST_EXPECT(ownerCount(env, issuer) == 1);
7075 env(trust(issuer, gwAUD(0)));
7076 env.close();
7077 BEAST_EXPECT(ownerCount(env, issuer) == 0);
7078
7079 // cheri attempts to accept becky's offers. Behavior with the
7080 // AutoTrustline NFT is uniform: issuer gets a new trust line.
7081 env(token::acceptSellOffer(cheri, beckyAutoTrustOfferIndex));
7082 env.close();
7083
7084 // Here's evidence that issuer got the new AUD trust line.
7085 BEAST_EXPECT(ownerCount(env, issuer) == 1);
7086 BEAST_EXPECT(env.balance(issuer, gwAUD) == gwAUD(5));
7087
7088 // issuer once again removes the trust line for AUD.
7089 env(pay(issuer, gw, gwAUD(5)));
7090 env.close();
7091 BEAST_EXPECT(ownerCount(env, issuer) == 0);
7092
7093 // cheri attempts to accept the NoAutoTrustLine NFT. Behavior
7094 // depends on whether fixEnforceNFTokenTrustline is enabled.
7095 if (feats[fixEnforceNFTokenTrustline])
7096 {
7097 // With fixEnforceNFTokenTrustline cheri can't accept the
7098 // offer because issuer could not get their transfer fee
7099 // without the appropriate trustline.
7100 env(token::acceptSellOffer(cheri, beckyNoAutoTrustOfferIndex),
7101 ter(tecNO_LINE));
7102 env.close();
7103
7104 // But if issuer re-establishes the trustline then the offer
7105 // can be accepted.
7106 env(trust(issuer, gwAUD(1000)));
7107 env.close();
7108 BEAST_EXPECT(ownerCount(env, issuer) == 1);
7109
7110 env(token::acceptSellOffer(cheri, beckyNoAutoTrustOfferIndex));
7111 env.close();
7112 }
7113 else
7114 {
7115 // Without fixEnforceNFTokenTrustline the offer just works
7116 // and issuer gets a trustline that they did not request.
7117 env(token::acceptSellOffer(cheri, beckyNoAutoTrustOfferIndex));
7118 env.close();
7119 }
7120 BEAST_EXPECT(ownerCount(env, issuer) == 1);
7121 BEAST_EXPECT(env.balance(issuer, gwAUD) == gwAUD(5));
7122 } // for feats
7123 }
7124
7125 void
7127 {
7128 testcase("Test fix NFT issuer is IOU issuer");
7129
7130 using namespace test::jtx;
7131
7132 Account const issuer{"issuer"};
7133 Account const becky{"becky"};
7134 Account const cheri{"cheri"};
7135 IOU const isISU(issuer["ISU"]);
7136
7137 // This test case covers issue...
7138 // https://github.com/XRPLF/rippled/issues/4941
7139 //
7140 // If an NFToken has a transfer fee then, when an offer is accepted,
7141 // a portion of the sale price goes to the issuer.
7142 //
7143 // It is possible for an issuer to issue both an IOU (for remittances)
7144 // and NFTokens. If the issuer's IOU is used to pay for the transfer
7145 // of one of the issuer's NFTokens, then paying the fee for that
7146 // transfer will fail with a tecNO_LINE.
7147 //
7148 // The problem occurs because the NFT code looks for a trust line to
7149 // pay the transfer fee. However the issuer of an IOU does not need
7150 // a trust line to accept their own issuance and, in fact, is not
7151 // allowed to have a trust line to themselves.
7152 //
7153 // This test looks at a situation where transfer of an NFToken is
7154 // prevented by this bug:
7155 // 1. Issuer issues an IOU (e.g, isISU).
7156 // 2. Becky and Cheri get trust lines for, and acquire, some isISU.
7157 // 3. Issuer mints NFToken with transfer fee.
7158 // 4. Becky acquires the NFToken, paying with XRP.
7159 // 5. Becky attempts to create an offer to sell the NFToken for
7160 // isISU(100). The attempt fails with `tecNO_LINE`.
7161 //
7162 // The featureNFTokenMintOffer amendment addresses this oversight.
7163 //
7164 // We remove the fixRemoveNFTokenAutoTrustLine amendment. Otherwise
7165 // we can't create NFTokens with tfTrustLine enabled.
7166 FeatureBitset const localFeatures =
7167 features - fixRemoveNFTokenAutoTrustLine;
7168
7169 Env env{*this, localFeatures};
7170 env.fund(XRP(1000), issuer, becky, cheri);
7171 env.close();
7172
7173 // Set trust lines so becky and cheri can use isISU.
7174 env(trust(becky, isISU(1000)));
7175 env(trust(cheri, isISU(1000)));
7176 env.close();
7177 env(pay(issuer, cheri, isISU(500)));
7178 env.close();
7179
7180 // issuer creates two NFTs: one with and one without AutoTrustLine.
7181 std::uint16_t xferFee = 5000; // 5%
7182 uint256 const nftAutoTrustID{token::getNextID(
7183 env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)};
7184 env(token::mint(issuer, 0u),
7185 token::xferFee(xferFee),
7186 txflags(tfTransferable | tfTrustLine));
7187 env.close();
7188
7189 uint256 const nftNoAutoTrustID{
7190 token::getNextID(env, issuer, 0u, tfTransferable, xferFee)};
7191 env(token::mint(issuer, 0u),
7192 token::xferFee(xferFee),
7193 txflags(tfTransferable));
7194 env.close();
7195
7196 // becky buys the nfts for 1 drop each.
7197 {
7198 uint256 const beckyBuyOfferIndex1 =
7199 keylet::nftoffer(becky, env.seq(becky)).key;
7200 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
7201 token::owner(issuer));
7202
7203 uint256 const beckyBuyOfferIndex2 =
7204 keylet::nftoffer(becky, env.seq(becky)).key;
7205 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
7206 token::owner(issuer));
7207
7208 env.close();
7209 env(token::acceptBuyOffer(issuer, beckyBuyOfferIndex1));
7210 env(token::acceptBuyOffer(issuer, beckyBuyOfferIndex2));
7211 env.close();
7212 }
7213
7214 // Behavior from here down diverges significantly based on
7215 // featureNFTokenMintOffer.
7216 if (!localFeatures[featureNFTokenMintOffer])
7217 {
7218 // Without featureNFTokenMintOffer becky simply can't
7219 // create an offer for a non-tfTrustLine NFToken that would
7220 // pay the transfer fee in issuer's own IOU.
7221 env(token::createOffer(becky, nftNoAutoTrustID, isISU(100)),
7222 txflags(tfSellNFToken),
7223 ter(tecNO_LINE));
7224 env.close();
7225
7226 // And issuer can't create a trust line to themselves.
7227 env(trust(issuer, isISU(1000)), ter(temDST_IS_SRC));
7228 env.close();
7229
7230 // However if the NFToken has the tfTrustLine flag set,
7231 // then becky can create the offer.
7232 uint256 const beckyAutoTrustOfferIndex =
7233 keylet::nftoffer(becky, env.seq(becky)).key;
7234 env(token::createOffer(becky, nftAutoTrustID, isISU(100)),
7235 txflags(tfSellNFToken));
7236 env.close();
7237
7238 // And cheri can accept the offer.
7239 env(token::acceptSellOffer(cheri, beckyAutoTrustOfferIndex));
7240 env.close();
7241
7242 // We verify that issuer got their transfer fee by seeing that
7243 // ISU(5) has disappeared out of cheri's and becky's balances.
7244 BEAST_EXPECT(env.balance(becky, isISU) == isISU(95));
7245 BEAST_EXPECT(env.balance(cheri, isISU) == isISU(400));
7246 }
7247 else
7248 {
7249 // With featureNFTokenMintOffer things go better.
7250 // becky creates offers to sell the nfts for ISU.
7251 uint256 const beckyNoAutoTrustOfferIndex =
7252 keylet::nftoffer(becky, env.seq(becky)).key;
7253 env(token::createOffer(becky, nftNoAutoTrustID, isISU(100)),
7254 txflags(tfSellNFToken));
7255 env.close();
7256 uint256 const beckyAutoTrustOfferIndex =
7257 keylet::nftoffer(becky, env.seq(becky)).key;
7258 env(token::createOffer(becky, nftAutoTrustID, isISU(100)),
7259 txflags(tfSellNFToken));
7260 env.close();
7261
7262 // cheri accepts becky's offers. Behavior is uniform:
7263 // issuer gets paid.
7264 env(token::acceptSellOffer(cheri, beckyAutoTrustOfferIndex));
7265 env.close();
7266
7267 // We verify that issuer got their transfer fee by seeing that
7268 // ISU(5) has disappeared out of cheri's and becky's balances.
7269 BEAST_EXPECT(env.balance(becky, isISU) == isISU(95));
7270 BEAST_EXPECT(env.balance(cheri, isISU) == isISU(400));
7271
7272 env(token::acceptSellOffer(cheri, beckyNoAutoTrustOfferIndex));
7273 env.close();
7274
7275 // We verify that issuer got their transfer fee by seeing that
7276 // an additional ISU(5) has disappeared out of cheri's and
7277 // becky's balances.
7278 BEAST_EXPECT(env.balance(becky, isISU) == isISU(190));
7279 BEAST_EXPECT(env.balance(cheri, isISU) == isISU(300));
7280 }
7281 }
7282
7283 void
7285 {
7286 testcase("Test NFTokenModify");
7287
7288 using namespace test::jtx;
7289
7290 Account const issuer{"issuer"};
7291 Account const alice("alice");
7292 Account const bob("bob");
7293
7294 bool const modifyEnabled = features[featureDynamicNFT];
7295
7296 {
7297 // Mint with tfMutable
7298 Env env{*this, features};
7299 env.fund(XRP(10000), issuer);
7300 env.close();
7301
7302 auto const expectedTer =
7303 modifyEnabled ? TER{tesSUCCESS} : TER{temINVALID_FLAG};
7304 env(token::mint(issuer, 0u), txflags(tfMutable), ter(expectedTer));
7305 env.close();
7306 }
7307 {
7308 Env env{*this, features};
7309 env.fund(XRP(10000), issuer);
7310 env.close();
7311
7312 // Modify a nftoken
7313 uint256 const nftId{token::getNextID(env, issuer, 0u, tfMutable)};
7314 if (modifyEnabled)
7315 {
7316 env(token::mint(issuer, 0u), txflags(tfMutable));
7317 env.close();
7318 BEAST_EXPECT(ownerCount(env, issuer) == 1);
7319 env(token::modify(issuer, nftId));
7320 BEAST_EXPECT(ownerCount(env, issuer) == 1);
7321 }
7322 else
7323 {
7324 env(token::mint(issuer, 0u));
7325 env.close();
7326 env(token::modify(issuer, nftId), ter(temDISABLED));
7327 env.close();
7328 }
7329 }
7330 if (!modifyEnabled)
7331 return;
7332
7333 {
7334 Env env{*this, features};
7335 env.fund(XRP(10000), issuer);
7336 env.close();
7337
7338 uint256 const nftId{token::getNextID(env, issuer, 0u, tfMutable)};
7339 env(token::mint(issuer, 0u), txflags(tfMutable));
7340 env.close();
7341
7342 // Set a negative fee. Exercises invalid preflight1.
7343 env(token::modify(issuer, nftId),
7344 fee(STAmount(10ull, true)),
7345 ter(temBAD_FEE));
7346 env.close();
7347
7348 // Invalid Flags
7349 env(token::modify(issuer, nftId),
7350 txflags(0x00000001),
7351 ter(temINVALID_FLAG));
7352
7353 // Invalid Owner
7354 env(token::modify(issuer, nftId),
7355 token::owner(issuer),
7356 ter(temMALFORMED));
7357 env.close();
7358
7359 // Invalid URI length = 0
7360 env(token::modify(issuer, nftId),
7361 token::uri(""),
7362 ter(temMALFORMED));
7363 env.close();
7364
7365 // Invalid URI length > 256
7366 env(token::modify(issuer, nftId),
7367 token::uri(std::string(maxTokenURILength + 1, 'q')),
7368 ter(temMALFORMED));
7369 env.close();
7370 }
7371 {
7372 Env env{*this, features};
7373 env.fund(XRP(10000), issuer, alice, bob);
7374 env.close();
7375
7376 {
7377 // NFToken not exists
7378 uint256 const nftIDNotExists{
7379 token::getNextID(env, issuer, 0u, tfMutable)};
7380 env.close();
7381
7382 env(token::modify(issuer, nftIDNotExists), ter(tecNO_ENTRY));
7383 env.close();
7384 }
7385 {
7386 // Invalid NFToken flag
7387 uint256 const nftIDNotModifiable{
7388 token::getNextID(env, issuer, 0u)};
7389 env(token::mint(issuer, 0u));
7390 env.close();
7391
7392 env(token::modify(issuer, nftIDNotModifiable),
7393 ter(tecNO_PERMISSION));
7394 env.close();
7395 }
7396 {
7397 // Unauthorized account
7398 uint256 const nftId{
7399 token::getNextID(env, issuer, 0u, tfMutable)};
7400 env(token::mint(issuer, 0u), txflags(tfMutable));
7401 env.close();
7402
7403 env(token::modify(bob, nftId),
7404 token::owner(issuer),
7405 ter(tecNO_PERMISSION));
7406 env.close();
7407
7408 env(token::setMinter(issuer, alice));
7409 env.close();
7410
7411 env(token::modify(bob, nftId),
7412 token::owner(issuer),
7413 ter(tecNO_PERMISSION));
7414 env.close();
7415 }
7416 }
7417 {
7418 Env env{*this, features};
7419 env.fund(XRP(10000), issuer, alice, bob);
7420 env.close();
7421
7422 // modify with tfFullyCanonicalSig should success
7423 uint256 const nftId{token::getNextID(env, issuer, 0u, tfMutable)};
7424 env(token::mint(issuer, 0u), txflags(tfMutable), token::uri("uri"));
7425 env.close();
7426
7427 env(token::modify(issuer, nftId), txflags(tfFullyCanonicalSig));
7428 env.close();
7429 }
7430 {
7431 Env env{*this, features};
7432 env.fund(XRP(10000), issuer, alice, bob);
7433 env.close();
7434
7435 // lambda that returns the JSON form of NFTokens held by acct
7436 auto accountNFTs = [&env](Account const& acct) {
7437 Json::Value params;
7438 params[jss::account] = acct.human();
7439 params[jss::type] = "state";
7440 auto response =
7441 env.rpc("json", "account_nfts", to_string(params));
7442 return response[jss::result][jss::account_nfts];
7443 };
7444
7445 // lambda that checks for the expected URI value of an NFToken
7446 auto checkURI = [&accountNFTs, this](
7447 Account const& acct,
7448 char const* uri,
7449 int line) {
7450 auto const nfts = accountNFTs(acct);
7451 if (nfts.size() == 1)
7452 pass();
7453 else
7454 {
7455 std::ostringstream text;
7456 text << "checkURI: unexpected NFT count on line " << line;
7457 fail(text.str(), __FILE__, line);
7458 return;
7459 }
7460
7461 if (uri == nullptr)
7462 {
7463 if (!nfts[0u].isMember(sfURI.jsonName))
7464 pass();
7465 else
7466 {
7467 std::ostringstream text;
7468 text << "checkURI: unexpected URI present on line "
7469 << line;
7470 fail(text.str(), __FILE__, line);
7471 }
7472 return;
7473 }
7474
7475 if (nfts[0u][sfURI.jsonName] == strHex(std::string(uri)))
7476 pass();
7477 else
7478 {
7479 std::ostringstream text;
7480 text << "checkURI: unexpected URI contents on line "
7481 << line;
7482 fail(text.str(), __FILE__, line);
7483 }
7484 };
7485
7486 uint256 const nftId{token::getNextID(env, issuer, 0u, tfMutable)};
7487 env.close();
7488
7489 env(token::mint(issuer, 0u), txflags(tfMutable), token::uri("uri"));
7490 env.close();
7491 checkURI(issuer, "uri", __LINE__);
7492
7493 // set URI Field
7494 env(token::modify(issuer, nftId), token::uri("new_uri"));
7495 env.close();
7496 checkURI(issuer, "new_uri", __LINE__);
7497
7498 // unset URI Field
7499 env(token::modify(issuer, nftId));
7500 env.close();
7501 checkURI(issuer, nullptr, __LINE__);
7502
7503 // set URI Field
7504 env(token::modify(issuer, nftId), token::uri("uri"));
7505 env.close();
7506 checkURI(issuer, "uri", __LINE__);
7507
7508 // Account != Owner
7509 uint256 const offerID =
7510 keylet::nftoffer(issuer, env.seq(issuer)).key;
7511 env(token::createOffer(issuer, nftId, XRP(0)),
7512 txflags(tfSellNFToken));
7513 env.close();
7514 env(token::acceptSellOffer(alice, offerID));
7515 env.close();
7516 BEAST_EXPECT(ownerCount(env, issuer) == 0);
7517 BEAST_EXPECT(ownerCount(env, alice) == 1);
7518 checkURI(alice, "uri", __LINE__);
7519
7520 // Modify by owner fails.
7521 env(token::modify(alice, nftId),
7522 token::uri("new_uri"),
7523 ter(tecNO_PERMISSION));
7524 env.close();
7525 BEAST_EXPECT(ownerCount(env, issuer) == 0);
7526 BEAST_EXPECT(ownerCount(env, alice) == 1);
7527 checkURI(alice, "uri", __LINE__);
7528
7529 env(token::modify(issuer, nftId),
7530 token::owner(alice),
7531 token::uri("new_uri"));
7532 env.close();
7533 BEAST_EXPECT(ownerCount(env, issuer) == 0);
7534 BEAST_EXPECT(ownerCount(env, alice) == 1);
7535 checkURI(alice, "new_uri", __LINE__);
7536
7537 env(token::modify(issuer, nftId), token::owner(alice));
7538 env.close();
7539 checkURI(alice, nullptr, __LINE__);
7540
7541 env(token::modify(issuer, nftId),
7542 token::owner(alice),
7543 token::uri("uri"));
7544 env.close();
7545 checkURI(alice, "uri", __LINE__);
7546
7547 // Modify by authorized minter
7548 env(token::setMinter(issuer, bob));
7549 env.close();
7550 env(token::modify(bob, nftId),
7551 token::owner(alice),
7552 token::uri("new_uri"));
7553 env.close();
7554 checkURI(alice, "new_uri", __LINE__);
7555
7556 env(token::modify(bob, nftId), token::owner(alice));
7557 env.close();
7558 checkURI(alice, nullptr, __LINE__);
7559
7560 env(token::modify(bob, nftId),
7561 token::owner(alice),
7562 token::uri("uri"));
7563 env.close();
7564 checkURI(alice, "uri", __LINE__);
7565 }
7566 }
7567
7568protected:
7570
7571 void
7573 {
7574 testEnabled(features);
7575 testMintReserve(features);
7576 testMintMaxTokens(features);
7577 testMintInvalid(features);
7578 testBurnInvalid(features);
7579 testCreateOfferInvalid(features);
7580 testCancelOfferInvalid(features);
7581 testAcceptOfferInvalid(features);
7582 testMintFlagBurnable(features);
7583 testMintFlagOnlyXRP(features);
7585 testMintFlagTransferable(features);
7586 testMintTransferFee(features);
7587 testMintTaxon(features);
7588 testMintURI(features);
7591 testCreateOfferExpiration(features);
7592 testCancelOffers(features);
7593 testCancelTooManyOffers(features);
7594 testBrokeredAccept(features);
7595 testNFTokenOfferOwner(features);
7596 testNFTokenWithTickets(features);
7597 testNFTokenDeleteAccount(features);
7598 testNftXxxOffers(features);
7599 testNFTokenNegOffer(features);
7600 testIOUWithTransferFee(features);
7601 testBrokeredSaleToSelf(features);
7602 testNFTokenRemint(features);
7603 testFeatMintWithOffer(features);
7604 testTxJsonMetaFields(features);
7607 testNFTIssuerIsIOUIssuer(features);
7608 testNFTokenModify(features);
7609 }
7610
7611public:
7612 void
7613 run() override
7614 {
7616 allFeatures - fixNFTokenReserve - featureNFTokenMintOffer -
7617 featureDynamicNFT);
7618 }
7619};
7620
7622{
7623 void
7624 run() override
7625 {
7627 allFeatures - featureDisallowIncoming - fixNFTokenReserve -
7628 featureNFTokenMintOffer - featureDynamicNFT);
7629 }
7630};
7631
7633{
7634 void
7635 run() override
7636 {
7638 allFeatures - featureNFTokenMintOffer - featureDynamicNFT);
7639 }
7640};
7641
7643{
7644 void
7645 run() override
7646 {
7647 testWithFeats(allFeatures - featureDynamicNFT);
7648 }
7649};
7650
7652{
7653 void
7654 run() override
7655 {
7657 }
7658};
7659
7660BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBaseUtil, app, ripple, 2);
7661BEAST_DEFINE_TESTSUITE_PRIO(NFTokenDisallowIncoming, app, ripple, 2);
7662BEAST_DEFINE_TESTSUITE_PRIO(NFTokenWOMintOffer, app, ripple, 2);
7663BEAST_DEFINE_TESTSUITE_PRIO(NFTokenWOModify, app, ripple, 2);
7664BEAST_DEFINE_TESTSUITE_PRIO(NFTokenAllFeatures, app, ripple, 2);
7665
7666} // namespace ripple
T back(T... args)
T back_inserter(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:41
A testsuite class.
Definition suite.h:52
void pass()
Record a successful test condition.
Definition suite.h:508
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:226
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:530
void run() override
Runs the suite.
void testCreateOfferDestination(FeatureBitset features)
std::uint32_t lastClose(test::jtx::Env &env)
FeatureBitset const allFeatures
void testMintInvalid(FeatureBitset features)
void testAcceptOfferInvalid(FeatureBitset features)
void testMintFlagTransferable(FeatureBitset features)
void testNFTokenRemint(FeatureBitset features)
void testCancelOffers(FeatureBitset features)
void testNFTIssuerIsIOUIssuer(FeatureBitset features)
void testMintTaxon(FeatureBitset features)
void testNFTokenModify(FeatureBitset features)
void testNFTokenDeleteAccount(FeatureBitset features)
void testUnaskedForAutoTrustline(FeatureBitset features)
FeatureBitset const disallowIncoming
void testFixNFTokenBuyerReserve(FeatureBitset features)
void testWithFeats(FeatureBitset features)
void testNFTokenOfferOwner(FeatureBitset features)
void testNFTokenWithTickets(FeatureBitset features)
void testCreateOfferDestinationDisallowIncoming(FeatureBitset features)
void testCreateOfferExpiration(FeatureBitset features)
void testMintMaxTokens(FeatureBitset features)
void testMintFlagCreateTrustLine(FeatureBitset features)
void testMintTransferFee(FeatureBitset features)
void testTxJsonMetaFields(FeatureBitset features)
void testNftXxxOffers(FeatureBitset features)
void testEnabled(FeatureBitset features)
void testNFTokenNegOffer(FeatureBitset features)
void testMintURI(FeatureBitset features)
void testCancelTooManyOffers(FeatureBitset features)
void testMintFlagBurnable(FeatureBitset features)
void testMintFlagOnlyXRP(FeatureBitset features)
void testMintReserve(FeatureBitset features)
static std::uint32_t nftCount(test::jtx::Env &env, test::jtx::Account const &acct)
static std::uint32_t ticketCount(test::jtx::Env const &env, test::jtx::Account const &acct)
void testBrokeredSaleToSelf(FeatureBitset features)
void testIOUWithTransferFee(FeatureBitset features)
static std::uint32_t burnedCount(test::jtx::Env const &env, test::jtx::Account const &issuer)
void testCreateOfferInvalid(FeatureBitset features)
static std::uint32_t mintedCount(test::jtx::Env const &env, test::jtx::Account const &issuer)
void testCancelOfferInvalid(FeatureBitset features)
void testBrokeredAccept(FeatureBitset features)
void testFeatMintWithOffer(FeatureBitset features)
void run() override
Runs the suite.
void testBurnInvalid(FeatureBitset features)
void run() override
Runs the suite.
void run() override
Runs the suite.
void run() override
Runs the suite.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:46
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition OpenView.cpp:150
void rawReplace(std::shared_ptr< SLE > const &sle) override
Unconditionally replace a state item.
Definition OpenView.cpp:224
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:753
static int const cMinOffset
Definition STAmount.h:46
static std::uint64_t const cMinValue
Definition STAmount.h:50
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:484
A type-safe wrap around standard integral types.
Immutable cryptographic account descriptor.
Definition Account.h:20
std::string const & human() const
Returns the human readable public key.
Definition Account.h:99
A transaction testing environment.
Definition Env.h:102
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:250
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
Account const & master
Definition Env.h:106
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:772
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:259
T clear(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T find(T... args)
T insert(T... args)
T is_same_v
@ arrayValue
array value (ordered list)
Definition json_value.h:25
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:408
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition Indexes.cpp:317
Taxon getTaxon(uint256 const &id)
Definition nft.h:89
Taxon toTaxon(std::uint32_t i)
Definition nft.h:23
std::uint32_t ownerCount(Env const &env, Account const &account)
FeatureBitset testable_amendments()
Definition Env.h:55
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::enable_if_t<(std::is_same< Byte, unsigned char >::value||std::is_same< Byte, std::uint8_t >::value), Byte > rand_byte()
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
constexpr std::uint32_t const tfOnlyXRP
Definition TxFlags.h:121
constexpr std::uint32_t asfDisallowIncomingNFTokenOffer
Definition TxFlags.h:71
constexpr std::uint32_t const tfSellNFToken
Definition TxFlags.h:211
std::size_t constexpr maxTokenOfferCancelCount
The maximum number of token offers that can be canceled at once.
Definition Protocol.h:52
@ lsfDisallowIncomingNFTokenOffer
std::uint16_t constexpr maxTransferFee
The maximum token transfer fee allowed.
Definition Protocol.h:66
constexpr std::uint32_t const tfBurnable
Definition TxFlags.h:120
@ tefNFTOKEN_IS_NOT_TRANSFERABLE
Definition TER.h:167
constexpr std::uint32_t const tfTrustLine
Definition TxFlags.h:122
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
std::size_t constexpr maxTokenURILength
The maximum length of a URI inside an NFT.
Definition Protocol.h:69
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:100
@ tecNO_ENTRY
Definition TER.h:288
@ tecNO_DST
Definition TER.h:272
@ tecOBJECT_NOT_FOUND
Definition TER.h:308
@ tecNO_ISSUER
Definition TER.h:281
@ tecTOO_SOON
Definition TER.h:300
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
Definition TER.h:305
@ tecUNFUNDED_OFFER
Definition TER.h:266
@ tecFROZEN
Definition TER.h:285
@ tecINSUFFICIENT_FUNDS
Definition TER.h:307
@ tecNFTOKEN_BUY_SELL_MISMATCH
Definition TER.h:304
@ tecNO_PERMISSION
Definition TER.h:287
@ tecHAS_OBLIGATIONS
Definition TER.h:299
@ tecNO_LINE
Definition TER.h:283
@ tecMAX_SEQUENCE_REACHED
Definition TER.h:302
@ tecINSUFFICIENT_PAYMENT
Definition TER.h:309
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
Definition TER.h:306
@ tecEXPIRED
Definition TER.h:296
@ tesSUCCESS
Definition TER.h:226
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr std::uint32_t tfFullyCanonicalSig
Transaction flags.
Definition TxFlags.h:41
TERSubset< CanCvtToTER > TER
Definition TER.h:630
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:99
constexpr std::uint32_t const tfMutable
Definition TxFlags.h:124
constexpr std::uint32_t const tfTransferable
Definition TxFlags.h:123
@ temBAD_AMOUNT
Definition TER.h:70
@ temBAD_FEE
Definition TER.h:73
@ temMALFORMED
Definition TER.h:68
@ temBAD_EXPIRATION
Definition TER.h:72
@ temINVALID_FLAG
Definition TER.h:92
@ temDISABLED
Definition TER.h:95
@ temDST_IS_SRC
Definition TER.h:89
@ temBAD_NFTOKEN_TRANSFER_FEE
Definition TER.h:108
T pop_back(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)
T str(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
T to_string(T... args)
T transform(T... args)