rippled
NFToken_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2021 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/tx/impl/details/NFTokenUtils.h>
21 #include <ripple/basics/random.h>
22 #include <ripple/protocol/Feature.h>
23 #include <ripple/protocol/jss.h>
24 #include <test/jtx.h>
25 
26 #include <initializer_list>
27 
28 namespace ripple {
29 
30 class NFToken_test : public beast::unit_test::suite
31 {
32  // Helper function that returns the owner count of an account root.
33  static std::uint32_t
34  ownerCount(test::jtx::Env const& env, test::jtx::Account const& acct)
35  {
36  std::uint32_t ret{0};
37  if (auto const sleAcct = env.le(acct))
38  ret = sleAcct->at(sfOwnerCount);
39  return ret;
40  }
41 
42  // Helper function that returns the number of NFTs minted by an issuer.
43  static std::uint32_t
44  mintedCount(test::jtx::Env const& env, test::jtx::Account const& issuer)
45  {
46  std::uint32_t ret{0};
47  if (auto const sleIssuer = env.le(issuer))
48  ret = sleIssuer->at(~sfMintedNFTokens).value_or(0);
49  return ret;
50  }
51 
52  // Helper function that returns the number of an issuer's burned NFTs.
53  static std::uint32_t
54  burnedCount(test::jtx::Env const& env, test::jtx::Account const& issuer)
55  {
56  std::uint32_t ret{0};
57  if (auto const sleIssuer = env.le(issuer))
58  ret = sleIssuer->at(~sfBurnedNFTokens).value_or(0);
59  return ret;
60  }
61 
62  // Helper function that returns the number of nfts owned by an account.
63  static std::uint32_t
65  {
66  Json::Value params;
67  params[jss::account] = acct.human();
68  params[jss::type] = "state";
69  Json::Value nfts = env.rpc("json", "account_nfts", to_string(params));
70  return nfts[jss::result][jss::account_nfts].size();
71  };
72 
73  // Helper function that returns the number of tickets held by an account.
74  static std::uint32_t
76  {
77  std::uint32_t ret{0};
78  if (auto const sleAcct = env.le(acct))
79  ret = sleAcct->at(~sfTicketCount).value_or(0);
80  return ret;
81  }
82 
83  // Helper function returns the close time of the parent ledger.
86  {
87  return env.current()->info().parentCloseTime.time_since_epoch().count();
88  }
89 
90  void
92  {
93  testcase("Enabled");
94 
95  using namespace test::jtx;
96  {
97  // If the NFT amendment is not enabled, you should not be able
98  // to create or burn NFTs.
99  Env env{*this, features - featureNonFungibleTokensV1};
100  Account const& master = env.master;
101 
102  BEAST_EXPECT(ownerCount(env, master) == 0);
103  BEAST_EXPECT(mintedCount(env, master) == 0);
104  BEAST_EXPECT(burnedCount(env, master) == 0);
105 
106  uint256 const nftId{token::getNextID(env, master, 0u)};
107  env(token::mint(master, 0u), ter(temDISABLED));
108  env.close();
109  BEAST_EXPECT(ownerCount(env, master) == 0);
110  BEAST_EXPECT(mintedCount(env, master) == 0);
111  BEAST_EXPECT(burnedCount(env, master) == 0);
112 
113  env(token::burn(master, nftId), ter(temDISABLED));
114  env.close();
115  BEAST_EXPECT(ownerCount(env, master) == 0);
116  BEAST_EXPECT(mintedCount(env, master) == 0);
117  BEAST_EXPECT(burnedCount(env, master) == 0);
118 
119  uint256 const offerIndex =
120  keylet::nftoffer(master, env.seq(master)).key;
121  env(token::createOffer(master, nftId, XRP(10)), ter(temDISABLED));
122  env.close();
123  BEAST_EXPECT(ownerCount(env, master) == 0);
124  BEAST_EXPECT(mintedCount(env, master) == 0);
125  BEAST_EXPECT(burnedCount(env, master) == 0);
126 
127  env(token::cancelOffer(master, {offerIndex}), ter(temDISABLED));
128  env.close();
129  BEAST_EXPECT(ownerCount(env, master) == 0);
130  BEAST_EXPECT(mintedCount(env, master) == 0);
131  BEAST_EXPECT(burnedCount(env, master) == 0);
132 
133  env(token::acceptBuyOffer(master, offerIndex), ter(temDISABLED));
134  env.close();
135  BEAST_EXPECT(ownerCount(env, master) == 0);
136  BEAST_EXPECT(mintedCount(env, master) == 0);
137  BEAST_EXPECT(burnedCount(env, master) == 0);
138  }
139  {
140  // If the NFT amendment is enabled all NFT-related
141  // facilities should be available.
142  Env env{*this, features};
143  Account const& master = env.master;
144 
145  BEAST_EXPECT(ownerCount(env, master) == 0);
146  BEAST_EXPECT(mintedCount(env, master) == 0);
147  BEAST_EXPECT(burnedCount(env, master) == 0);
148 
149  uint256 const nftId0{token::getNextID(env, env.master, 0u)};
150  env(token::mint(env.master, 0u));
151  env.close();
152  BEAST_EXPECT(ownerCount(env, master) == 1);
153  BEAST_EXPECT(mintedCount(env, master) == 1);
154  BEAST_EXPECT(burnedCount(env, master) == 0);
155 
156  env(token::burn(env.master, nftId0));
157  env.close();
158  BEAST_EXPECT(ownerCount(env, master) == 0);
159  BEAST_EXPECT(mintedCount(env, master) == 1);
160  BEAST_EXPECT(burnedCount(env, master) == 1);
161 
162  uint256 const nftId1{
163  token::getNextID(env, env.master, 0u, tfTransferable)};
164  env(token::mint(env.master, 0u), txflags(tfTransferable));
165  env.close();
166  BEAST_EXPECT(ownerCount(env, master) == 1);
167  BEAST_EXPECT(mintedCount(env, master) == 2);
168  BEAST_EXPECT(burnedCount(env, master) == 1);
169 
170  Account const alice{"alice"};
171  env.fund(XRP(10000), alice);
172  env.close();
173  uint256 const aliceOfferIndex =
174  keylet::nftoffer(alice, env.seq(alice)).key;
175  env(token::createOffer(alice, nftId1, XRP(1000)),
176  token::owner(master));
177  env.close();
178 
179  BEAST_EXPECT(ownerCount(env, master) == 1);
180  BEAST_EXPECT(mintedCount(env, master) == 2);
181  BEAST_EXPECT(burnedCount(env, master) == 1);
182 
183  BEAST_EXPECT(ownerCount(env, alice) == 1);
184  BEAST_EXPECT(mintedCount(env, alice) == 0);
185  BEAST_EXPECT(burnedCount(env, alice) == 0);
186 
187  env(token::acceptBuyOffer(master, aliceOfferIndex));
188  env.close();
189 
190  BEAST_EXPECT(ownerCount(env, master) == 0);
191  BEAST_EXPECT(mintedCount(env, master) == 2);
192  BEAST_EXPECT(burnedCount(env, master) == 1);
193 
194  BEAST_EXPECT(ownerCount(env, alice) == 1);
195  BEAST_EXPECT(mintedCount(env, alice) == 0);
196  BEAST_EXPECT(burnedCount(env, alice) == 0);
197  }
198  }
199 
200  void
202  {
203  // Verify that the reserve behaves as expected for minting.
204  testcase("Mint reserve");
205 
206  using namespace test::jtx;
207 
208  Env env{*this, features};
209  Account const alice{"alice"};
210  Account const minter{"minter"};
211 
212  // Fund alice and minter enough to exist, but not enough to meet
213  // the reserve for creating their first NFT. Account reserve for unit
214  // tests is 200 XRP, not 20.
215  env.fund(XRP(200), alice, minter);
216  env.close();
217  BEAST_EXPECT(env.balance(alice) == XRP(200));
218  BEAST_EXPECT(env.balance(minter) == XRP(200));
219  BEAST_EXPECT(ownerCount(env, alice) == 0);
220  BEAST_EXPECT(ownerCount(env, minter) == 0);
221 
222  // alice does not have enough XRP to cover the reserve for an NFT page.
223  env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
224  env.close();
225  BEAST_EXPECT(ownerCount(env, alice) == 0);
226  BEAST_EXPECT(mintedCount(env, alice) == 0);
227  BEAST_EXPECT(burnedCount(env, alice) == 0);
228 
229  // Pay alice almost enough to make the reserve for an NFT page.
230  env(pay(env.master, alice, XRP(50) + drops(9)));
231  env.close();
232 
233  // A lambda that checks alice's ownerCount, mintedCount, and
234  // burnedCount all in one fell swoop.
235  auto checkAliceOwnerMintedBurned = [&env, this, &alice](
236  std::uint32_t owners,
237  std::uint32_t minted,
238  std::uint32_t burned,
239  int line) {
240  auto oneCheck =
241  [line, this](
242  char const* type, std::uint32_t found, std::uint32_t exp) {
243  if (found == exp)
244  pass();
245  else
246  {
248  ss << "Wrong " << type << " count. Found: " << found
249  << "; Expected: " << exp;
250  fail(ss.str(), __FILE__, line);
251  }
252  };
253  oneCheck("owner", ownerCount(env, alice), owners);
254  oneCheck("minted", mintedCount(env, alice), minted);
255  oneCheck("burned", burnedCount(env, alice), burned);
256  };
257 
258  // alice still does not have enough XRP for the reserve of an NFT page.
259  env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
260  env.close();
261  checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
262 
263  // Pay alice enough to make the reserve for an NFT page.
264  env(pay(env.master, alice, drops(11)));
265  env.close();
266 
267  // Now alice can mint an NFT.
268  env(token::mint(alice));
269  env.close();
270  checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
271 
272  // Alice should be able to mint an additional 31 NFTs without
273  // any additional reserve requirements.
274  for (int i = 1; i < 32; ++i)
275  {
276  env(token::mint(alice));
277  checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
278  }
279 
280  // That NFT page is full. Creating an additional NFT page requires
281  // additional reserve.
282  env(token::mint(alice), ter(tecINSUFFICIENT_RESERVE));
283  env.close();
284  checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
285 
286  // Pay alice almost enough to make the reserve for an NFT page.
287  env(pay(env.master, alice, XRP(50) + drops(329)));
288  env.close();
289 
290  // alice still does not have enough XRP for the reserve of an NFT page.
291  env(token::mint(alice), ter(tecINSUFFICIENT_RESERVE));
292  env.close();
293  checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
294 
295  // Pay alice enough to make the reserve for an NFT page.
296  env(pay(env.master, alice, drops(11)));
297  env.close();
298 
299  // Now alice can mint an NFT.
300  env(token::mint(alice));
301  env.close();
302  checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
303 
304  // alice burns the NFTs she created: check that pages consolidate
305  std::uint32_t seq = 0;
306 
307  while (seq < 33)
308  {
309  env(token::burn(alice, token::getID(alice, 0, seq++)));
310  env.close();
311  checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
312  }
313 
314  // alice burns a non-existent NFT.
315  env(token::burn(alice, token::getID(alice, 197, 5)), ter(tecNO_ENTRY));
316  env.close();
317  checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
318 
319  // That was fun! Now let's see what happens when we let someone else
320  // mint NFTs on alice's behalf. alice gives permission to minter.
321  env(token::setMinter(alice, minter));
322  env.close();
323  BEAST_EXPECT(
324  env.le(alice)->getAccountID(sfNFTokenMinter) == minter.id());
325 
326  // A lambda that checks minter's and alice's ownerCount,
327  // mintedCount, and burnedCount all in one fell swoop.
328  auto checkMintersOwnerMintedBurned = [&env, this, &alice, &minter](
329  std::uint32_t aliceOwners,
330  std::uint32_t aliceMinted,
331  std::uint32_t aliceBurned,
332  std::uint32_t minterOwners,
333  std::uint32_t minterMinted,
334  std::uint32_t minterBurned,
335  int line) {
336  auto oneCheck = [this](
337  char const* type,
338  std::uint32_t found,
339  std::uint32_t exp,
340  int line) {
341  if (found == exp)
342  pass();
343  else
344  {
346  ss << "Wrong " << type << " count. Found: " << found
347  << "; Expected: " << exp;
348  fail(ss.str(), __FILE__, line);
349  }
350  };
351  oneCheck("alice owner", ownerCount(env, alice), aliceOwners, line);
352  oneCheck(
353  "alice minted", mintedCount(env, alice), aliceMinted, line);
354  oneCheck(
355  "alice burned", burnedCount(env, alice), aliceBurned, line);
356  oneCheck(
357  "minter owner", ownerCount(env, minter), minterOwners, line);
358  oneCheck(
359  "minter minted", mintedCount(env, minter), minterMinted, line);
360  oneCheck(
361  "minter burned", burnedCount(env, minter), minterBurned, line);
362  };
363 
364  std::uint32_t nftSeq = 33;
365 
366  // Pay minter almost enough to make the reserve for an NFT page.
367  env(pay(env.master, minter, XRP(50) - drops(1)));
368  env.close();
369  checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
370 
371  // minter still does not have enough XRP for the reserve of an NFT page.
372  // Just for grins (and code coverage), minter mints NFTs that include
373  // a URI.
374  env(token::mint(minter),
375  token::issuer(alice),
376  token::uri("uri"),
378  env.close();
379  checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
380 
381  // Pay minter enough to make the reserve for an NFT page.
382  env(pay(env.master, minter, drops(11)));
383  env.close();
384 
385  // Now minter can mint an NFT for alice.
386  env(token::mint(minter), token::issuer(alice), token::uri("uri"));
387  env.close();
388  checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
389 
390  // Minter should be able to mint an additional 31 NFTs for alice
391  // without any additional reserve requirements.
392  for (int i = 1; i < 32; ++i)
393  {
394  env(token::mint(minter), token::issuer(alice), token::uri("uri"));
395  checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
396  }
397 
398  // Pay minter almost enough for the reserve of an additional NFT page.
399  env(pay(env.master, minter, XRP(50) + drops(319)));
400  env.close();
401 
402  // That NFT page is full. Creating an additional NFT page requires
403  // additional reserve.
404  env(token::mint(minter),
405  token::issuer(alice),
406  token::uri("uri"),
408  env.close();
409  checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
410 
411  // Pay minter enough for the reserve of an additional NFT page.
412  env(pay(env.master, minter, drops(11)));
413  env.close();
414 
415  // Now minter can mint an NFT.
416  env(token::mint(minter), token::issuer(alice), token::uri("uri"));
417  env.close();
418  checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
419 
420  // minter burns the NFTs she created.
421  while (nftSeq < 65)
422  {
423  env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
424  env.close();
425  checkMintersOwnerMintedBurned(
426  0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
427  }
428 
429  // minter has one more NFT to burn. Should take her owner count to 0.
430  env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
431  env.close();
432  checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
433 
434  // minter burns a non-existent NFT.
435  env(token::burn(minter, token::getID(alice, 2009, 3)),
436  ter(tecNO_ENTRY));
437  env.close();
438  checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
439  }
440 
441  void
443  {
444  // Make sure that an account cannot cause the sfMintedNFTokens
445  // field to wrap by minting more than 0xFFFF'FFFF tokens.
446  testcase("Mint max tokens");
447 
448  using namespace test::jtx;
449 
450  Account const alice{"alice"};
451  Env env{*this, features};
452  env.fund(XRP(1000), alice);
453  env.close();
454 
455  // We're going to hack the ledger in order to avoid generating
456  // 4 billion or so NFTs. Because we're hacking the ledger we
457  // need alice's account to have non-zero sfMintedNFTokens and
458  // sfBurnedNFTokens fields. This prevents an exception when the
459  // AccountRoot template is applied.
460  {
461  uint256 const nftId0{token::getNextID(env, alice, 0u)};
462  env(token::mint(alice, 0u));
463  env.close();
464 
465  env(token::burn(alice, nftId0));
466  env.close();
467  }
468 
469  // Note that we're bypassing almost all of the ledger's safety
470  // checks with this modify() call. If you call close() between
471  // here and the end of the test all the effort will be lost.
472  env.app().openLedger().modify(
473  [&alice](OpenView& view, beast::Journal j) {
474  // Get the account root we want to hijack.
475  auto const sle = view.read(keylet::account(alice.id()));
476  if (!sle)
477  return false; // This would be really surprising!
478 
479  // Just for sanity's sake we'll check that the current value
480  // of sfMintedNFTokens matches what we expect.
481  auto replacement = std::make_shared<SLE>(*sle, sle->key());
482  if (replacement->getFieldU32(sfMintedNFTokens) != 1)
483  return false; // Unexpected test conditions.
484 
485  // Now replace sfMintedNFTokens with the largest valid value.
486  (*replacement)[sfMintedNFTokens] = 0xFFFF'FFFE;
487  view.rawReplace(replacement);
488  return true;
489  });
490 
491  // See whether alice is at the boundary that causes an error.
492  env(token::mint(alice, 0u), ter(tesSUCCESS));
493  env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
494  }
495 
496  void
497  testMintInvalid(FeatureBitset features)
498  {
499  // Explore many of the invalid ways to mint an NFT.
500  testcase("Mint invalid");
501 
502  using namespace test::jtx;
503 
504  Env env{*this, features};
505  Account const alice{"alice"};
506  Account const minter{"minter"};
507 
508  // Fund alice and minter enough to exist, but not enough to meet
509  // the reserve for creating their first NFT. Account reserve for unit
510  // tests is 200 XRP, not 20.
511  env.fund(XRP(200), alice, minter);
512  env.close();
513 
514  env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
515  env.close();
516 
517  // Fund alice enough to start minting NFTs.
518  env(pay(env.master, alice, XRP(1000)));
519  env.close();
520 
521  //----------------------------------------------------------------------
522  // preflight
523 
524  // Set a negative fee.
525  env(token::mint(alice, 0u),
526  fee(STAmount(10ull, true)),
527  ter(temBAD_FEE));
528 
529  // Set an invalid flag.
530  env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
531 
532  // Can't set a transfer fee if the NFT does not have the tfTRANSFERABLE
533  // flag set.
534  env(token::mint(alice, 0u),
535  token::xferFee(maxTransferFee),
536  ter(temMALFORMED));
537 
538  // Set a bad transfer fee.
539  env(token::mint(alice, 0u),
540  token::xferFee(maxTransferFee + 1),
541  txflags(tfTransferable),
543 
544  // Account can't also be issuer.
545  env(token::mint(alice, 0u), token::issuer(alice), ter(temMALFORMED));
546 
547  // Invalid URI: zero length.
548  env(token::mint(alice, 0u), token::uri(""), ter(temMALFORMED));
549 
550  // Invalid URI: too long.
551  env(token::mint(alice, 0u),
552  token::uri(std::string(maxTokenURILength + 1, 'q')),
553  ter(temMALFORMED));
554 
555  //----------------------------------------------------------------------
556  // preflight
557 
558  // Non-existent issuer.
559  env(token::mint(alice, 0u),
560  token::issuer(Account("demon")),
561  ter(tecNO_ISSUER));
562 
563  //----------------------------------------------------------------------
564  // doApply
565 
566  // Existent issuer, but not given minting permission
567  env(token::mint(minter, 0u),
568  token::issuer(alice),
569  ter(tecNO_PERMISSION));
570  }
571 
572  void
574  {
575  // Explore many of the invalid ways to burn an NFT.
576  testcase("Burn invalid");
577 
578  using namespace test::jtx;
579 
580  Env env{*this, features};
581  Account const alice{"alice"};
582  Account const buyer{"buyer"};
583  Account const minter{"minter"};
584  Account const gw("gw");
585  IOU const gwAUD(gw["AUD"]);
586 
587  // Fund alice and minter enough to exist and create an NFT, but not
588  // enough to meet the reserve for creating their first NFTOffer.
589  // Account reserve for unit tests is 200 XRP, not 20.
590  env.fund(XRP(250), alice, buyer, minter, gw);
591  env.close();
592  BEAST_EXPECT(ownerCount(env, alice) == 0);
593 
594  uint256 const nftAlice0ID =
595  token::getNextID(env, alice, 0, tfTransferable);
596  env(token::mint(alice, 0u), txflags(tfTransferable));
597  env.close();
598  BEAST_EXPECT(ownerCount(env, alice) == 1);
599 
600  //----------------------------------------------------------------------
601  // preflight
602 
603  // Set a negative fee.
604  env(token::burn(alice, nftAlice0ID),
605  fee(STAmount(10ull, true)),
606  ter(temBAD_FEE));
607  env.close();
608  BEAST_EXPECT(ownerCount(env, alice) == 1);
609 
610  // Set an invalid flag.
611  env(token::burn(alice, nftAlice0ID),
612  txflags(0x00008000),
613  ter(temINVALID_FLAG));
614  env.close();
615  BEAST_EXPECT(ownerCount(env, buyer) == 0);
616 
617  //----------------------------------------------------------------------
618  // preclaim
619 
620  // Try to burn a token that doesn't exist.
621  env(token::burn(alice, token::getID(alice, 0, 1)), ter(tecNO_ENTRY));
622  env.close();
623  BEAST_EXPECT(ownerCount(env, buyer) == 0);
624 
625  // Can't burn a token with many buy or sell offers. But that is
626  // verified in testManyNftOffers().
627 
628  //----------------------------------------------------------------------
629  // doApply
630  }
631 
632  void
634  {
635  testcase("Invalid NFT offer create");
636 
637  using namespace test::jtx;
638 
639  Env env{*this, features};
640  Account const alice{"alice"};
641  Account const buyer{"buyer"};
642  Account const gw("gw");
643  IOU const gwAUD(gw["AUD"]);
644 
645  // Fund alice enough to exist and create an NFT, but not
646  // enough to meet the reserve for creating their first NFTOffer.
647  // Account reserve for unit tests is 200 XRP, not 20.
648  env.fund(XRP(250), alice, buyer, gw);
649  env.close();
650  BEAST_EXPECT(ownerCount(env, alice) == 0);
651 
652  uint256 const nftAlice0ID =
653  token::getNextID(env, alice, 0, tfTransferable, 10);
654  env(token::mint(alice, 0u),
655  txflags(tfTransferable),
656  token::xferFee(10));
657  env.close();
658  BEAST_EXPECT(ownerCount(env, alice) == 1);
659 
660  uint256 const nftXrpOnlyID =
661  token::getNextID(env, alice, 0, tfOnlyXRP | tfTransferable);
662  env(token::mint(alice, 0), txflags(tfOnlyXRP | tfTransferable));
663  env.close();
664  BEAST_EXPECT(ownerCount(env, alice) == 1);
665 
666  uint256 nftNoXferID = token::getNextID(env, alice, 0);
667  env(token::mint(alice, 0));
668  env.close();
669  BEAST_EXPECT(ownerCount(env, alice) == 1);
670 
671  //----------------------------------------------------------------------
672  // preflight
673 
674  // buyer burns a fee, so they no longer have enough XRP to cover the
675  // reserve for a token offer.
676  env(noop(buyer));
677  env.close();
678 
679  // buyer tries to create an NFTokenOffer, but doesn't have the reserve.
680  env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
681  token::owner(alice),
683  env.close();
684  BEAST_EXPECT(ownerCount(env, buyer) == 0);
685 
686  // Set a negative fee.
687  env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
688  fee(STAmount(10ull, true)),
689  ter(temBAD_FEE));
690  env.close();
691  BEAST_EXPECT(ownerCount(env, buyer) == 0);
692 
693  // Set an invalid flag.
694  env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
695  txflags(0x00008000),
696  ter(temINVALID_FLAG));
697  env.close();
698  BEAST_EXPECT(ownerCount(env, buyer) == 0);
699 
700  // Set an invalid amount.
701  env(token::createOffer(buyer, nftXrpOnlyID, buyer["USD"](1)),
702  ter(temBAD_AMOUNT));
703  env(token::createOffer(buyer, nftAlice0ID, buyer["USD"](0)),
704  ter(temBAD_AMOUNT));
705  env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
706  ter(temBAD_AMOUNT));
707  env.close();
708  BEAST_EXPECT(ownerCount(env, buyer) == 0);
709 
710  // Set a bad expiration.
711  env(token::createOffer(buyer, nftAlice0ID, buyer["USD"](1)),
712  token::expiration(0),
713  ter(temBAD_EXPIRATION));
714  env.close();
715  BEAST_EXPECT(ownerCount(env, buyer) == 0);
716 
717  // Invalid Owner field and tfSellToken flag relationships.
718  // A buy offer must specify the owner.
719  env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
720  ter(temMALFORMED));
721  env.close();
722  BEAST_EXPECT(ownerCount(env, buyer) == 0);
723 
724  // A sell offer must not specify the owner; the owner is implicit.
725  env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
726  token::owner(alice),
727  txflags(tfSellNFToken),
728  ter(temMALFORMED));
729  env.close();
730  BEAST_EXPECT(ownerCount(env, alice) == 1);
731 
732  // An owner may not offer to buy their own token.
733  env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
734  token::owner(alice),
735  ter(temMALFORMED));
736  env.close();
737  BEAST_EXPECT(ownerCount(env, alice) == 1);
738 
739  // The destination may not be the account submitting the transaction.
740  env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
741  token::destination(alice),
742  txflags(tfSellNFToken),
743  ter(temMALFORMED));
744  env.close();
745  BEAST_EXPECT(ownerCount(env, alice) == 1);
746 
747  // The destination must be an account already established in the ledger.
748  env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
749  token::destination(Account("demon")),
750  txflags(tfSellNFToken),
751  ter(tecNO_DST));
752  env.close();
753  BEAST_EXPECT(ownerCount(env, alice) == 1);
754 
755  //----------------------------------------------------------------------
756  // preclaim
757 
758  // The new NFTokenOffer may not have passed its expiration time.
759  env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
760  token::owner(alice),
761  token::expiration(lastClose(env)),
762  ter(tecEXPIRED));
763  env.close();
764  BEAST_EXPECT(ownerCount(env, buyer) == 0);
765 
766  // The nftID must be present in the ledger.
767  env(token::createOffer(buyer, token::getID(alice, 0, 1), XRP(1000)),
768  token::owner(alice),
769  ter(tecNO_ENTRY));
770  env.close();
771  BEAST_EXPECT(ownerCount(env, buyer) == 0);
772 
773  // The nftID must be present in the ledger of a sell offer too.
774  env(token::createOffer(alice, token::getID(alice, 0, 1), XRP(1000)),
775  txflags(tfSellNFToken),
776  ter(tecNO_ENTRY));
777  env.close();
778  BEAST_EXPECT(ownerCount(env, buyer) == 0);
779 
780  // buyer must have the funds to pay for their offer.
781  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
782  token::owner(alice),
783  ter(tecNO_LINE));
784  env.close();
785  BEAST_EXPECT(ownerCount(env, buyer) == 0);
786 
787  env(trust(buyer, gwAUD(1000)));
788  env.close();
789  BEAST_EXPECT(ownerCount(env, buyer) == 1);
790  env.close();
791 
792  // Issuer (alice) must have a trust line for the offered funds.
793  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
794  token::owner(alice),
795  ter(tecNO_LINE));
796  env.close();
797  BEAST_EXPECT(ownerCount(env, buyer) == 1);
798 
799  // Give alice the needed trust line, but freeze it.
800  env(trust(gw, alice["AUD"](999), tfSetFreeze));
801  env.close();
802 
803  // Issuer (alice) must have a trust line for the offered funds and
804  // the trust line may not be frozen.
805  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
806  token::owner(alice),
807  ter(tecFROZEN));
808  env.close();
809  BEAST_EXPECT(ownerCount(env, buyer) == 1);
810 
811  // Unfreeze alice's trustline.
812  env(trust(gw, alice["AUD"](999), tfClearFreeze));
813  env.close();
814 
815  // Can't transfer the NFT if the transferable flag is not set.
816  env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
817  token::owner(alice),
819  env.close();
820  BEAST_EXPECT(ownerCount(env, buyer) == 1);
821 
822  // Give buyer the needed trust line, but freeze it.
823  env(trust(gw, buyer["AUD"](999), tfSetFreeze));
824  env.close();
825 
826  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
827  token::owner(alice),
828  ter(tecFROZEN));
829  env.close();
830  BEAST_EXPECT(ownerCount(env, buyer) == 1);
831 
832  // Unfreeze buyer's trust line, but buyer has no actual gwAUD.
833  // to cover the offer.
834  env(trust(gw, buyer["AUD"](999), tfClearFreeze));
835  env(trust(buyer, gwAUD(1000)));
836  env.close();
837 
838  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
839  token::owner(alice),
840  ter(tecUNFUNDED_OFFER));
841  env.close();
842  BEAST_EXPECT(ownerCount(env, buyer) == 1); // the trust line.
843 
844  //----------------------------------------------------------------------
845  // doApply
846 
847  // Give buyer almost enough AUD to cover the offer...
848  env(pay(gw, buyer, gwAUD(999)));
849  env.close();
850 
851  // However buyer doesn't have enough XRP to cover the reserve for
852  // an NFT offer.
853  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
854  token::owner(alice),
856  env.close();
857  BEAST_EXPECT(ownerCount(env, buyer) == 1);
858 
859  // Give buyer almost enough XRP to cover the reserve.
860  env(pay(env.master, buyer, XRP(50) + drops(119)));
861  env.close();
862 
863  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
864  token::owner(alice),
866  env.close();
867  BEAST_EXPECT(ownerCount(env, buyer) == 1);
868 
869  // Give buyer just enough XRP to cover the reserve for the offer.
870  env(pay(env.master, buyer, drops(11)));
871  env.close();
872 
873  // We don't care whether the offer is fully funded until the offer is
874  // accepted. Success at last!
875  env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
876  token::owner(alice),
877  ter(tesSUCCESS));
878  env.close();
879  BEAST_EXPECT(ownerCount(env, buyer) == 2);
880  }
881 
882  void
884  {
885  testcase("Invalid NFT offer cancel");
886 
887  using namespace test::jtx;
888 
889  Env env{*this, features};
890  Account const alice{"alice"};
891  Account const buyer{"buyer"};
892  Account const gw("gw");
893  IOU const gwAUD(gw["AUD"]);
894 
895  env.fund(XRP(1000), alice, buyer, gw);
896  env.close();
897  BEAST_EXPECT(ownerCount(env, alice) == 0);
898 
899  uint256 const nftAlice0ID =
900  token::getNextID(env, alice, 0, tfTransferable);
901  env(token::mint(alice, 0u), txflags(tfTransferable));
902  env.close();
903  BEAST_EXPECT(ownerCount(env, alice) == 1);
904 
905  // This is the offer we'll try to cancel.
906  uint256 const buyerOfferIndex =
907  keylet::nftoffer(buyer, env.seq(buyer)).key;
908  env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
909  token::owner(alice),
910  ter(tesSUCCESS));
911  env.close();
912  BEAST_EXPECT(ownerCount(env, buyer) == 1);
913 
914  //----------------------------------------------------------------------
915  // preflight
916 
917  // Set a negative fee.
918  env(token::cancelOffer(buyer, {buyerOfferIndex}),
919  fee(STAmount(10ull, true)),
920  ter(temBAD_FEE));
921  env.close();
922  BEAST_EXPECT(ownerCount(env, buyer) == 1);
923 
924  // Set an invalid flag.
925  env(token::cancelOffer(buyer, {buyerOfferIndex}),
926  txflags(0x00008000),
927  ter(temINVALID_FLAG));
928  env.close();
929  BEAST_EXPECT(ownerCount(env, buyer) == 1);
930 
931  // Empty list of tokens to delete.
932  {
933  Json::Value jv = token::cancelOffer(buyer);
935  env(jv, ter(temMALFORMED));
936  env.close();
937  BEAST_EXPECT(ownerCount(env, buyer) == 1);
938  }
939 
940  // List of tokens to delete is too long.
941  {
942  std::vector<uint256> offers(
943  maxTokenOfferCancelCount + 1, buyerOfferIndex);
944 
945  env(token::cancelOffer(buyer, offers), ter(temMALFORMED));
946  env.close();
947  BEAST_EXPECT(ownerCount(env, buyer) == 1);
948  }
949 
950  // Duplicate entries are not allowed in the list of offers to cancel.
951  env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
952  ter(temMALFORMED));
953  env.close();
954  BEAST_EXPECT(ownerCount(env, buyer) == 1);
955 
956  // Provide neither offers to cancel nor a root index.
957  env(token::cancelOffer(buyer), ter(temMALFORMED));
958  env.close();
959  BEAST_EXPECT(ownerCount(env, buyer) == 1);
960 
961  //----------------------------------------------------------------------
962  // preclaim
963 
964  // Make a non-root directory that we can pass as a root index.
965  env(pay(env.master, gw, XRP(5000)));
966  env.close();
967  for (std::uint32_t i = 1; i < 34; ++i)
968  {
969  env(offer(gw, XRP(i), gwAUD(1)));
970  env.close();
971  }
972 
973  {
974  // gw attempts to cancel a Check as through it is an NFTokenOffer.
975  auto const gwCheckId = keylet::check(gw, env.seq(gw)).key;
976  env(check::create(gw, env.master, XRP(300)));
977  env.close();
978 
979  env(token::cancelOffer(gw, {gwCheckId}), ter(tecNO_PERMISSION));
980  env.close();
981 
982  // Cancel the check so it doesn't mess up later tests.
983  env(check::cancel(gw, gwCheckId));
984  env.close();
985  }
986 
987  // gw attempts to cancel an offer they don't have permission to cancel.
988  env(token::cancelOffer(gw, {buyerOfferIndex}), ter(tecNO_PERMISSION));
989  env.close();
990  BEAST_EXPECT(ownerCount(env, buyer) == 1);
991 
992  //----------------------------------------------------------------------
993  // doApply
994  //
995  // The tefBAD_LEDGER conditions are too hard to test.
996  // But let's see a successful offer cancel.
997  env(token::cancelOffer(buyer, {buyerOfferIndex}));
998  env.close();
999  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1000  }
1001 
1002  void
1004  {
1005  testcase("Invalid NFT offer accept");
1006 
1007  using namespace test::jtx;
1008 
1009  Env env{*this, features};
1010  Account const alice{"alice"};
1011  Account const buyer{"buyer"};
1012  Account const gw("gw");
1013  IOU const gwAUD(gw["AUD"]);
1014 
1015  env.fund(XRP(1000), alice, buyer, gw);
1016  env.close();
1017  BEAST_EXPECT(ownerCount(env, alice) == 0);
1018 
1019  uint256 const nftAlice0ID =
1020  token::getNextID(env, alice, 0, tfTransferable);
1021  env(token::mint(alice, 0u), txflags(tfTransferable));
1022  env.close();
1023  BEAST_EXPECT(ownerCount(env, alice) == 1);
1024 
1025  uint256 const nftXrpOnlyID =
1026  token::getNextID(env, alice, 0, tfOnlyXRP | tfTransferable);
1027  env(token::mint(alice, 0), txflags(tfOnlyXRP | tfTransferable));
1028  env.close();
1029  BEAST_EXPECT(ownerCount(env, alice) == 1);
1030 
1031  uint256 nftNoXferID = token::getNextID(env, alice, 0);
1032  env(token::mint(alice, 0));
1033  env.close();
1034  BEAST_EXPECT(ownerCount(env, alice) == 1);
1035 
1036  // alice creates sell offers for her nfts.
1037  uint256 const plainOfferIndex =
1038  keylet::nftoffer(alice, env.seq(alice)).key;
1039  env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1040  txflags(tfSellNFToken));
1041  env.close();
1042  BEAST_EXPECT(ownerCount(env, alice) == 2);
1043 
1044  uint256 const audOfferIndex =
1045  keylet::nftoffer(alice, env.seq(alice)).key;
1046  env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1047  txflags(tfSellNFToken));
1048  env.close();
1049  BEAST_EXPECT(ownerCount(env, alice) == 3);
1050 
1051  uint256 const xrpOnlyOfferIndex =
1052  keylet::nftoffer(alice, env.seq(alice)).key;
1053  env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1054  txflags(tfSellNFToken));
1055  env.close();
1056  BEAST_EXPECT(ownerCount(env, alice) == 4);
1057 
1058  uint256 const noXferOfferIndex =
1059  keylet::nftoffer(alice, env.seq(alice)).key;
1060  env(token::createOffer(alice, nftNoXferID, XRP(30)),
1061  txflags(tfSellNFToken));
1062  env.close();
1063  BEAST_EXPECT(ownerCount(env, alice) == 5);
1064 
1065  // alice creates a sell offer that will expire soon.
1066  uint256 const aliceExpOfferIndex =
1067  keylet::nftoffer(alice, env.seq(alice)).key;
1068  env(token::createOffer(alice, nftNoXferID, XRP(40)),
1069  txflags(tfSellNFToken),
1070  token::expiration(lastClose(env) + 5));
1071  env.close();
1072  BEAST_EXPECT(ownerCount(env, alice) == 6);
1073 
1074  //----------------------------------------------------------------------
1075  // preflight
1076 
1077  // Set a negative fee.
1078  env(token::acceptSellOffer(buyer, noXferOfferIndex),
1079  fee(STAmount(10ull, true)),
1080  ter(temBAD_FEE));
1081  env.close();
1082  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1083 
1084  // Set an invalid flag.
1085  env(token::acceptSellOffer(buyer, noXferOfferIndex),
1086  txflags(0x00008000),
1087  ter(temINVALID_FLAG));
1088  env.close();
1089  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1090 
1091  // Supply nether an sfNFTokenBuyOffer nor an sfNFTokenSellOffer field.
1092  {
1093  Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1095  env(jv, ter(temMALFORMED));
1096  env.close();
1097  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1098  }
1099 
1100  // A buy offer may not contain a sfNFTokenBrokerFee field.
1101  {
1102  Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1105  env(jv, ter(temMALFORMED));
1106  env.close();
1107  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1108  }
1109 
1110  // A sell offer may not contain a sfNFTokenBrokerFee field.
1111  {
1112  Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1115  env(jv, ter(temMALFORMED));
1116  env.close();
1117  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1118  }
1119 
1120  // A brokered offer may not contain a negative or zero brokerFee.
1121  env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1122  token::brokerFee(gwAUD(0)),
1123  ter(temMALFORMED));
1124  env.close();
1125  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1126 
1127  //----------------------------------------------------------------------
1128  // preclaim
1129 
1130  // The buy offer must be present in the ledger.
1131  uint256 const missingOfferIndex = keylet::nftoffer(alice, 1).key;
1132  env(token::acceptBuyOffer(buyer, missingOfferIndex),
1133  ter(tecOBJECT_NOT_FOUND));
1134  env.close();
1135  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1136 
1137  // The buy offer must not have expired.
1138  env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
1139  env.close();
1140  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1141 
1142  // The sell offer must be present in the ledger.
1143  env(token::acceptSellOffer(buyer, missingOfferIndex),
1144  ter(tecOBJECT_NOT_FOUND));
1145  env.close();
1146  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1147 
1148  // The sell offer must not have expired.
1149  env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
1150  env.close();
1151  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1152 
1153  //----------------------------------------------------------------------
1154  // preclaim brokered
1155 
1156  // alice and buyer need trustlines before buyer can to create an
1157  // offer for gwAUD.
1158  env(trust(alice, gwAUD(1000)));
1159  env(trust(buyer, gwAUD(1000)));
1160  env.close();
1161  env(pay(gw, buyer, gwAUD(30)));
1162  env.close();
1163  BEAST_EXPECT(ownerCount(env, alice) == 7);
1164  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1165 
1166  // We're about to exercise offer brokering, so we need
1167  // corresponding buy and sell offers.
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(29)),
1173  token::owner(alice));
1174  env.close();
1175  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1176 
1177  // gw attempts to broker offers that are not for the same token.
1178  env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1180  env.close();
1181  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1182 
1183  // gw attempts to broker offers that are not for the same currency.
1184  env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1186  env.close();
1187  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1188 
1189  // In a brokered offer, the buyer must offer greater than or
1190  // equal to the selling price.
1191  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1193  env.close();
1194  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1195 
1196  // Remove buyer's offer.
1197  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1198  env.close();
1199  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1200  }
1201  {
1202  // buyer creates a buy offer for one of alice's nfts.
1203  uint256 const buyerOfferIndex =
1204  keylet::nftoffer(buyer, env.seq(buyer)).key;
1205  env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1206  token::owner(alice));
1207  env.close();
1208  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1209 
1210  // Broker sets their fee in a denomination other than the one
1211  // used by the offers
1212  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1213  token::brokerFee(XRP(40)),
1215  env.close();
1216  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1217 
1218  // Broker fee way too big.
1219  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1220  token::brokerFee(gwAUD(31)),
1222  env.close();
1223  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1224 
1225  // Broker fee is smaller, but still too big once the offer
1226  // seller's minimum is taken into account.
1227  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1228  token::brokerFee(gwAUD(1.5)),
1230  env.close();
1231  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1232 
1233  // Remove buyer's offer.
1234  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1235  env.close();
1236  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1237  }
1238  //----------------------------------------------------------------------
1239  // preclaim buy
1240  {
1241  // buyer creates a buy offer for one of alice's nfts.
1242  uint256 const buyerOfferIndex =
1243  keylet::nftoffer(buyer, env.seq(buyer)).key;
1244  env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1245  token::owner(alice));
1246  env.close();
1247  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1248 
1249  // Don't accept a buy offer if the sell flag is set.
1250  env(token::acceptBuyOffer(buyer, plainOfferIndex),
1252  env.close();
1253  BEAST_EXPECT(ownerCount(env, alice) == 7);
1254 
1255  // An account can't accept its own offer.
1256  env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1258  env.close();
1259  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1260 
1261  // An offer acceptor must have enough funds to pay for the offer.
1262  env(pay(buyer, gw, gwAUD(30)));
1263  env.close();
1264  BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1265  env(token::acceptBuyOffer(alice, buyerOfferIndex),
1266  ter(tecINSUFFICIENT_FUNDS));
1267  env.close();
1268  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1269 
1270  // alice gives her NFT to gw, so alice no longer owns nftAlice0.
1271  {
1272  uint256 const offerIndex =
1273  keylet::nftoffer(alice, env.seq(alice)).key;
1274  env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1275  txflags(tfSellNFToken));
1276  env.close();
1277  env(token::acceptSellOffer(gw, offerIndex));
1278  env.close();
1279  BEAST_EXPECT(ownerCount(env, alice) == 7);
1280  }
1281  env(pay(gw, buyer, gwAUD(30)));
1282  env.close();
1283 
1284  // alice can't accept a buy offer for an NFT she no longer owns.
1285  env(token::acceptBuyOffer(alice, buyerOfferIndex),
1286  ter(tecNO_PERMISSION));
1287  env.close();
1288  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1289 
1290  // Remove buyer's offer.
1291  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1292  env.close();
1293  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1294  }
1295  //----------------------------------------------------------------------
1296  // preclaim sell
1297  {
1298  // buyer creates a buy offer for one of alice's nfts.
1299  uint256 const buyerOfferIndex =
1300  keylet::nftoffer(buyer, env.seq(buyer)).key;
1301  env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1302  token::owner(alice));
1303  env.close();
1304  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1305 
1306  // Don't accept a sell offer without the sell flag set.
1307  env(token::acceptSellOffer(alice, buyerOfferIndex),
1309  env.close();
1310  BEAST_EXPECT(ownerCount(env, alice) == 7);
1311 
1312  // An account can't accept its own offer.
1313  env(token::acceptSellOffer(alice, plainOfferIndex),
1315  env.close();
1316  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1317 
1318  // The seller must currently be in possession of the token they
1319  // are selling. alice gave nftAlice0ID to gw.
1320  env(token::acceptSellOffer(buyer, plainOfferIndex),
1321  ter(tecNO_PERMISSION));
1322  env.close();
1323  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1324 
1325  // gw gives nftAlice0ID back to alice. That allows us to check
1326  // buyer attempting to accept one of alice's offers with
1327  // insufficient funds.
1328  {
1329  uint256 const offerIndex =
1330  keylet::nftoffer(gw, env.seq(gw)).key;
1331  env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1332  txflags(tfSellNFToken));
1333  env.close();
1334  env(token::acceptSellOffer(alice, offerIndex));
1335  env.close();
1336  BEAST_EXPECT(ownerCount(env, alice) == 7);
1337  }
1338  env(pay(buyer, gw, gwAUD(30)));
1339  env.close();
1340  BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1341  env(token::acceptSellOffer(buyer, audOfferIndex),
1342  ter(tecINSUFFICIENT_FUNDS));
1343  env.close();
1344  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1345  }
1346 
1347  //----------------------------------------------------------------------
1348  // doApply
1349  //
1350  // As far as I can see none of the failure modes are accessible as
1351  // long as the preflight and preclaim conditions are met.
1352  }
1353 
1354  void
1356  {
1357  // Exercise NFTs with flagBurnable set and not set.
1358  testcase("Mint flagBurnable");
1359 
1360  using namespace test::jtx;
1361 
1362  Env env{*this, features};
1363  Account const alice{"alice"};
1364  Account const buyer{"buyer"};
1365  Account const minter1{"minter1"};
1366  Account const minter2{"minter2"};
1367 
1368  env.fund(XRP(1000), alice, buyer, minter1, minter2);
1369  env.close();
1370  BEAST_EXPECT(ownerCount(env, alice) == 0);
1371 
1372  // alice selects minter as her minter.
1373  env(token::setMinter(alice, minter1));
1374  env.close();
1375 
1376  // A lambda that...
1377  // 1. creates an alice nft
1378  // 2. minted by minter and
1379  // 3. transfers that nft to buyer.
1380  auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1381  std::uint32_t flags) {
1382  uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1383  env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1384  env.close();
1385 
1386  uint256 const offerIndex =
1387  keylet::nftoffer(minter1, env.seq(minter1)).key;
1388  env(token::createOffer(minter1, nftID, XRP(0)),
1389  txflags(tfSellNFToken));
1390  env.close();
1391 
1392  env(token::acceptSellOffer(buyer, offerIndex));
1393  env.close();
1394 
1395  return nftID;
1396  };
1397 
1398  // An NFT without flagBurnable can only be burned by its owner.
1399  {
1400  uint256 const noBurnID = nftToBuyer(0);
1401  env(token::burn(alice, noBurnID),
1402  token::owner(buyer),
1403  ter(tecNO_PERMISSION));
1404  env.close();
1405  env(token::burn(minter1, noBurnID),
1406  token::owner(buyer),
1407  ter(tecNO_PERMISSION));
1408  env.close();
1409  env(token::burn(minter2, noBurnID),
1410  token::owner(buyer),
1411  ter(tecNO_PERMISSION));
1412  env.close();
1413 
1414  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1415  env(token::burn(buyer, noBurnID), token::owner(buyer));
1416  env.close();
1417  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1418  }
1419  // An NFT with flagBurnable can be burned by the issuer.
1420  {
1421  uint256 const burnableID = nftToBuyer(tfBurnable);
1422  env(token::burn(minter2, burnableID),
1423  token::owner(buyer),
1424  ter(tecNO_PERMISSION));
1425  env.close();
1426 
1427  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1428  env(token::burn(alice, burnableID), token::owner(buyer));
1429  env.close();
1430  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1431  }
1432  // An NFT with flagBurnable can be burned by the owner.
1433  {
1434  uint256 const burnableID = nftToBuyer(tfBurnable);
1435  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1436  env(token::burn(buyer, burnableID));
1437  env.close();
1438  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1439  }
1440  // An NFT with flagBurnable can be burned by the minter.
1441  {
1442  uint256 const burnableID = nftToBuyer(tfBurnable);
1443  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1444  env(token::burn(buyer, burnableID), token::owner(buyer));
1445  env.close();
1446  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1447  }
1448  // An nft with flagBurnable may be burned by the issuers' minter,
1449  // who may not be the original minter.
1450  {
1451  uint256 const burnableID = nftToBuyer(tfBurnable);
1452  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1453 
1454  env(token::setMinter(alice, minter2));
1455  env.close();
1456 
1457  // minter1 is no longer alice's minter, so no longer has
1458  // permisson to burn alice's nfts.
1459  env(token::burn(minter1, burnableID),
1460  token::owner(buyer),
1461  ter(tecNO_PERMISSION));
1462  env.close();
1463  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1464 
1465  // minter2, however, can burn alice's nfts.
1466  env(token::burn(minter2, burnableID), token::owner(buyer));
1467  env.close();
1468  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1469  }
1470  }
1471 
1472  void
1474  {
1475  // Exercise NFTs with flagOnlyXRP set and not set.
1476  testcase("Mint flagOnlyXRP");
1477 
1478  using namespace test::jtx;
1479 
1480  Env env{*this, features};
1481  Account const alice{"alice"};
1482  Account const buyer{"buyer"};
1483  Account const gw("gw");
1484  IOU const gwAUD(gw["AUD"]);
1485 
1486  // Set trust lines so alice and buyer can use gwAUD.
1487  env.fund(XRP(1000), alice, buyer, gw);
1488  env.close();
1489  env(trust(alice, gwAUD(1000)));
1490  env(trust(buyer, gwAUD(1000)));
1491  env.close();
1492  env(pay(gw, buyer, gwAUD(100)));
1493 
1494  // Don't set flagOnlyXRP and offers can be made with IOUs.
1495  {
1496  uint256 const nftIOUsOkayID{
1497  token::getNextID(env, alice, 0u, tfTransferable)};
1498  env(token::mint(alice, 0u), txflags(tfTransferable));
1499  env.close();
1500 
1501  BEAST_EXPECT(ownerCount(env, alice) == 2);
1502  uint256 const aliceOfferIndex =
1503  keylet::nftoffer(alice, env.seq(alice)).key;
1504  env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1505  txflags(tfSellNFToken));
1506  env.close();
1507  BEAST_EXPECT(ownerCount(env, alice) == 3);
1508 
1509  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1510  uint256 const buyerOfferIndex =
1511  keylet::nftoffer(buyer, env.seq(buyer)).key;
1512  env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1513  token::owner(alice));
1514  env.close();
1515  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1516 
1517  // Cancel the two offers just to be tidy.
1518  env(token::cancelOffer(alice, {aliceOfferIndex}));
1519  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1520  env.close();
1521  BEAST_EXPECT(ownerCount(env, alice) == 2);
1522  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1523 
1524  // Also burn alice's nft.
1525  env(token::burn(alice, nftIOUsOkayID));
1526  env.close();
1527  BEAST_EXPECT(ownerCount(env, alice) == 1);
1528  }
1529 
1530  // Set flagOnlyXRP and offers using IOUs are rejected.
1531  {
1532  uint256 const nftOnlyXRPID{
1533  token::getNextID(env, alice, 0u, tfOnlyXRP | tfTransferable)};
1534  env(token::mint(alice, 0u), txflags(tfOnlyXRP | tfTransferable));
1535  env.close();
1536 
1537  BEAST_EXPECT(ownerCount(env, alice) == 2);
1538  env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1539  txflags(tfSellNFToken),
1540  ter(temBAD_AMOUNT));
1541  env.close();
1542  BEAST_EXPECT(ownerCount(env, alice) == 2);
1543 
1544  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1545  env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1546  token::owner(alice),
1547  ter(temBAD_AMOUNT));
1548  env.close();
1549  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1550 
1551  // However offers for XRP are okay.
1552  BEAST_EXPECT(ownerCount(env, alice) == 2);
1553  env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1554  txflags(tfSellNFToken));
1555  env.close();
1556  BEAST_EXPECT(ownerCount(env, alice) == 3);
1557 
1558  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1559  env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1560  token::owner(alice));
1561  env.close();
1562  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1563  }
1564  }
1565 
1566  void
1568  {
1569  // Exercise NFTs with flagCreateTrustLines set and not set.
1570  testcase("Mint flagCreateTrustLines");
1571 
1572  using namespace test::jtx;
1573 
1574  Env env{*this, features};
1575  Account const alice{"alice"};
1576  Account const becky{"becky"};
1577  Account const cheri{"cheri"};
1578  Account const gw("gw");
1579  IOU const gwAUD(gw["AUD"]);
1580  IOU const gwCAD(gw["CAD"]);
1581  IOU const gwEUR(gw["EUR"]);
1582 
1583  env.fund(XRP(1000), alice, becky, cheri, gw);
1584  env.close();
1585 
1586  // Set trust lines so becky and cheri can use gw's currency.
1587  env(trust(becky, gwAUD(1000)));
1588  env(trust(cheri, gwAUD(1000)));
1589  env(trust(becky, gwCAD(1000)));
1590  env(trust(cheri, gwCAD(1000)));
1591  env(trust(becky, gwEUR(1000)));
1592  env(trust(cheri, gwEUR(1000)));
1593  env.close();
1594  env(pay(gw, becky, gwAUD(500)));
1595  env(pay(gw, becky, gwCAD(500)));
1596  env(pay(gw, becky, gwEUR(500)));
1597  env(pay(gw, cheri, gwAUD(500)));
1598  env(pay(gw, cheri, gwCAD(500)));
1599  env.close();
1600 
1601  // An nft without flagCreateTrustLines but with a non-zero transfer
1602  // fee will not allow creating offers that use IOUs for payment.
1603  for (std::uint32_t xferFee : {0, 1})
1604  {
1605  uint256 const nftNoAutoTrustID{
1606  token::getNextID(env, alice, 0u, tfTransferable, xferFee)};
1607  env(token::mint(alice, 0u),
1608  token::xferFee(xferFee),
1609  txflags(tfTransferable));
1610  env.close();
1611 
1612  // becky buys the nft for 1 drop.
1613  uint256 const beckyBuyOfferIndex =
1614  keylet::nftoffer(becky, env.seq(becky)).key;
1615  env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1616  token::owner(alice));
1617  env.close();
1618  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1619  env.close();
1620 
1621  // becky attempts to sell the nft for AUD.
1622  TER const createOfferTER =
1623  xferFee ? TER(tecNO_LINE) : TER(tesSUCCESS);
1624  uint256 const beckyOfferIndex =
1625  keylet::nftoffer(becky, env.seq(becky)).key;
1626  env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1627  txflags(tfSellNFToken),
1628  ter(createOfferTER));
1629  env.close();
1630 
1631  // cheri offers to buy the nft for CAD.
1632  uint256 const cheriOfferIndex =
1633  keylet::nftoffer(cheri, env.seq(cheri)).key;
1634  env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1635  token::owner(becky),
1636  ter(createOfferTER));
1637  env.close();
1638 
1639  // To keep things tidy, cancel the offers.
1640  env(token::cancelOffer(becky, {beckyOfferIndex}));
1641  env(token::cancelOffer(cheri, {cheriOfferIndex}));
1642  env.close();
1643  }
1644  // An nft with flagCreateTrustLines but with a non-zero transfer
1645  // fee allows transfers using IOUs for payment.
1646  {
1647  std::uint16_t transferFee = 10000; // 10%
1648 
1649  uint256 const nftAutoTrustID{token::getNextID(
1650  env, alice, 0u, tfTransferable | tfTrustLine, transferFee)};
1651  env(token::mint(alice, 0u),
1652  token::xferFee(transferFee),
1653  txflags(tfTransferable | tfTrustLine));
1654  env.close();
1655 
1656  // becky buys the nft for 1 drop.
1657  uint256 const beckyBuyOfferIndex =
1658  keylet::nftoffer(becky, env.seq(becky)).key;
1659  env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1660  token::owner(alice));
1661  env.close();
1662  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1663  env.close();
1664 
1665  // becky sells the nft for AUD.
1666  uint256 const beckySellOfferIndex =
1667  keylet::nftoffer(becky, env.seq(becky)).key;
1668  env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1669  txflags(tfSellNFToken));
1670  env.close();
1671  env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1672  env.close();
1673 
1674  // alice should now have a trust line for gwAUD.
1675  BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1676 
1677  // becky buys the nft back for CAD.
1678  uint256 const beckyBuyBackOfferIndex =
1679  keylet::nftoffer(becky, env.seq(becky)).key;
1680  env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1681  token::owner(cheri));
1682  env.close();
1683  env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1684  env.close();
1685 
1686  // alice should now have a trust line for gwAUD and gwCAD.
1687  BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1688  BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1689  }
1690  // Now that alice has trust lines already established, an nft without
1691  // flagCreateTrustLines will work for preestablished trust lines.
1692  {
1693  std::uint16_t transferFee = 5000; // 5%
1694  uint256 const nftNoAutoTrustID{
1695  token::getNextID(env, alice, 0u, tfTransferable, transferFee)};
1696  env(token::mint(alice, 0u),
1697  token::xferFee(transferFee),
1698  txflags(tfTransferable));
1699  env.close();
1700 
1701  // alice sells the nft using AUD.
1702  uint256 const aliceSellOfferIndex =
1703  keylet::nftoffer(alice, env.seq(alice)).key;
1704  env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1705  txflags(tfSellNFToken));
1706  env.close();
1707  env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1708  env.close();
1709 
1710  // alice should now have AUD(210):
1711  // o 200 for this sale and
1712  // o 10 for the previous sale's fee.
1713  BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1714 
1715  // cheri can't sell the NFT for EUR, but can for CAD.
1716  env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1717  txflags(tfSellNFToken),
1718  ter(tecNO_LINE));
1719  env.close();
1720  uint256 const cheriSellOfferIndex =
1721  keylet::nftoffer(cheri, env.seq(cheri)).key;
1722  env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1723  txflags(tfSellNFToken));
1724  env.close();
1725  env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1726  env.close();
1727 
1728  // alice should now have CAD(10):
1729  // o 5 from this sale's fee and
1730  // o 5 for the previous sale's fee.
1731  BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1732  }
1733  }
1734 
1735  void
1737  {
1738  // Exercise NFTs with flagTransferable set and not set.
1739  testcase("Mint flagTransferable");
1740 
1741  using namespace test::jtx;
1742 
1743  Env env{*this, features};
1744 
1745  Account const alice{"alice"};
1746  Account const becky{"becky"};
1747  Account const minter{"minter"};
1748 
1749  env.fund(XRP(1000), alice, becky, minter);
1750  env.close();
1751 
1752  // First try an nft made by alice without flagTransferable set.
1753  {
1754  BEAST_EXPECT(ownerCount(env, alice) == 0);
1755  uint256 const nftAliceNoTransferID{
1756  token::getNextID(env, alice, 0u)};
1757  env(token::mint(alice, 0u), token::xferFee(0));
1758  env.close();
1759  BEAST_EXPECT(ownerCount(env, alice) == 1);
1760 
1761  // becky tries to offer to buy alice's nft.
1762  BEAST_EXPECT(ownerCount(env, becky) == 0);
1763  env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1764  token::owner(alice),
1766 
1767  // alice offers to sell the nft and becky accepts the offer.
1768  uint256 const aliceSellOfferIndex =
1769  keylet::nftoffer(alice, env.seq(alice)).key;
1770  env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1771  txflags(tfSellNFToken));
1772  env.close();
1773  env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1774  env.close();
1775  BEAST_EXPECT(ownerCount(env, alice) == 0);
1776  BEAST_EXPECT(ownerCount(env, becky) == 1);
1777 
1778  // becky tries to offer the nft for sale.
1779  env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1780  txflags(tfSellNFToken),
1782  env.close();
1783  BEAST_EXPECT(ownerCount(env, alice) == 0);
1784  BEAST_EXPECT(ownerCount(env, becky) == 1);
1785 
1786  // becky tries to offer the nft for sale with alice as the
1787  // destination. That also doesn't work.
1788  env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1789  txflags(tfSellNFToken),
1790  token::destination(alice),
1792  env.close();
1793  BEAST_EXPECT(ownerCount(env, alice) == 0);
1794  BEAST_EXPECT(ownerCount(env, becky) == 1);
1795 
1796  // alice offers to buy the nft back from becky. becky accepts
1797  // the offer.
1798  uint256 const aliceBuyOfferIndex =
1799  keylet::nftoffer(alice, env.seq(alice)).key;
1800  env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1801  token::owner(becky));
1802  env.close();
1803  env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1804  env.close();
1805  BEAST_EXPECT(ownerCount(env, alice) == 1);
1806  BEAST_EXPECT(ownerCount(env, becky) == 0);
1807 
1808  // alice burns her nft so accounting is simpler below.
1809  env(token::burn(alice, nftAliceNoTransferID));
1810  env.close();
1811  BEAST_EXPECT(ownerCount(env, alice) == 0);
1812  BEAST_EXPECT(ownerCount(env, becky) == 0);
1813  }
1814  // Try an nft minted by minter for alice without flagTransferable set.
1815  {
1816  env(token::setMinter(alice, minter));
1817  env.close();
1818 
1819  BEAST_EXPECT(ownerCount(env, minter) == 0);
1820  uint256 const nftMinterNoTransferID{
1821  token::getNextID(env, alice, 0u)};
1822  env(token::mint(minter), token::issuer(alice));
1823  env.close();
1824  BEAST_EXPECT(ownerCount(env, minter) == 1);
1825 
1826  // becky tries to offer to buy minter's nft.
1827  BEAST_EXPECT(ownerCount(env, becky) == 0);
1828  env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1829  token::owner(minter),
1831  env.close();
1832  BEAST_EXPECT(ownerCount(env, becky) == 0);
1833 
1834  // alice removes authorization of minter.
1835  env(token::clearMinter(alice));
1836  env.close();
1837 
1838  // minter tries to offer their nft for sale.
1839  BEAST_EXPECT(ownerCount(env, minter) == 1);
1840  env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1841  txflags(tfSellNFToken),
1843  env.close();
1844  BEAST_EXPECT(ownerCount(env, minter) == 1);
1845 
1846  // Let enough ledgers pass that old transactions are no longer
1847  // retried, then alice gives authorization back to minter.
1848  for (int i = 0; i < 10; ++i)
1849  env.close();
1850 
1851  env(token::setMinter(alice, minter));
1852  env.close();
1853  BEAST_EXPECT(ownerCount(env, minter) == 1);
1854 
1855  // minter successfully offers their nft for sale.
1856  BEAST_EXPECT(ownerCount(env, minter) == 1);
1857  uint256 const minterSellOfferIndex =
1858  keylet::nftoffer(minter, env.seq(minter)).key;
1859  env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1860  txflags(tfSellNFToken));
1861  env.close();
1862  BEAST_EXPECT(ownerCount(env, minter) == 2);
1863 
1864  // alice removes authorization of minter so we can see whether
1865  // minter's pre-existing offer still works.
1866  env(token::clearMinter(alice));
1867  env.close();
1868 
1869  // becky buys minter's nft even though minter is no longer alice's
1870  // official minter.
1871  BEAST_EXPECT(ownerCount(env, becky) == 0);
1872  env(token::acceptSellOffer(becky, minterSellOfferIndex));
1873  env.close();
1874  BEAST_EXPECT(ownerCount(env, becky) == 1);
1875  BEAST_EXPECT(ownerCount(env, minter) == 0);
1876 
1877  // becky attempts to sell the nft.
1878  env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1879  txflags(tfSellNFToken),
1881  env.close();
1882 
1883  // Since minter is not, at the moment, alice's official minter
1884  // they cannot create an offer to buy the nft they minted.
1885  BEAST_EXPECT(ownerCount(env, minter) == 0);
1886  env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1887  token::owner(becky),
1889  env.close();
1890  BEAST_EXPECT(ownerCount(env, minter) == 0);
1891 
1892  // alice can create an offer to buy the nft.
1893  BEAST_EXPECT(ownerCount(env, alice) == 0);
1894  uint256 const aliceBuyOfferIndex =
1895  keylet::nftoffer(alice, env.seq(alice)).key;
1896  env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1897  token::owner(becky));
1898  env.close();
1899  BEAST_EXPECT(ownerCount(env, alice) == 1);
1900 
1901  // Let enough ledgers pass that old transactions are no longer
1902  // retried, then alice gives authorization back to minter.
1903  for (int i = 0; i < 10; ++i)
1904  env.close();
1905 
1906  env(token::setMinter(alice, minter));
1907  env.close();
1908 
1909  // Now minter can create an offer to buy the nft.
1910  BEAST_EXPECT(ownerCount(env, minter) == 0);
1911  uint256 const minterBuyOfferIndex =
1912  keylet::nftoffer(minter, env.seq(minter)).key;
1913  env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1914  token::owner(becky));
1915  env.close();
1916  BEAST_EXPECT(ownerCount(env, minter) == 1);
1917 
1918  // alice removes authorization of minter so we can see whether
1919  // minter's pre-existing buy offer still works.
1920  env(token::clearMinter(alice));
1921  env.close();
1922 
1923  // becky accepts minter's sell offer.
1924  BEAST_EXPECT(ownerCount(env, minter) == 1);
1925  BEAST_EXPECT(ownerCount(env, becky) == 1);
1926  env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1927  env.close();
1928  BEAST_EXPECT(ownerCount(env, minter) == 1);
1929  BEAST_EXPECT(ownerCount(env, becky) == 0);
1930  BEAST_EXPECT(ownerCount(env, alice) == 1);
1931 
1932  // minter burns their nft and alice cancels her offer so the
1933  // next tests can start with a clean slate.
1934  env(token::burn(minter, nftMinterNoTransferID), ter(tesSUCCESS));
1935  env.close();
1936  env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1937  env.close();
1938  BEAST_EXPECT(ownerCount(env, alice) == 0);
1939  BEAST_EXPECT(ownerCount(env, becky) == 0);
1940  BEAST_EXPECT(ownerCount(env, minter) == 0);
1941  }
1942  // nfts with flagTransferable set should be buyable and salable
1943  // by anybody.
1944  {
1945  BEAST_EXPECT(ownerCount(env, alice) == 0);
1946  uint256 const nftAliceID{
1947  token::getNextID(env, alice, 0u, tfTransferable)};
1948  env(token::mint(alice, 0u), txflags(tfTransferable));
1949  env.close();
1950  BEAST_EXPECT(ownerCount(env, alice) == 1);
1951 
1952  // Both alice and becky can make offers for alice's nft.
1953  uint256 const aliceSellOfferIndex =
1954  keylet::nftoffer(alice, env.seq(alice)).key;
1955  env(token::createOffer(alice, nftAliceID, XRP(20)),
1956  txflags(tfSellNFToken));
1957  env.close();
1958  BEAST_EXPECT(ownerCount(env, alice) == 2);
1959 
1960  uint256 const beckyBuyOfferIndex =
1961  keylet::nftoffer(becky, env.seq(becky)).key;
1962  env(token::createOffer(becky, nftAliceID, XRP(21)),
1963  token::owner(alice));
1964  env.close();
1965  BEAST_EXPECT(ownerCount(env, alice) == 2);
1966 
1967  // becky accepts alice's sell offer.
1968  env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1969  env.close();
1970  BEAST_EXPECT(ownerCount(env, alice) == 0);
1971  BEAST_EXPECT(ownerCount(env, becky) == 2);
1972 
1973  // becky offers to sell the nft.
1974  uint256 const beckySellOfferIndex =
1975  keylet::nftoffer(becky, env.seq(becky)).key;
1976  env(token::createOffer(becky, nftAliceID, XRP(22)),
1977  txflags(tfSellNFToken));
1978  env.close();
1979  BEAST_EXPECT(ownerCount(env, alice) == 0);
1980  BEAST_EXPECT(ownerCount(env, becky) == 3);
1981 
1982  // minter buys the nft (even though minter is not currently
1983  // alice's minter).
1984  env(token::acceptSellOffer(minter, beckySellOfferIndex));
1985  env.close();
1986  BEAST_EXPECT(ownerCount(env, alice) == 0);
1987  BEAST_EXPECT(ownerCount(env, becky) == 1);
1988  BEAST_EXPECT(ownerCount(env, minter) == 1);
1989 
1990  // minter offers to sell the nft.
1991  uint256 const minterSellOfferIndex =
1992  keylet::nftoffer(minter, env.seq(minter)).key;
1993  env(token::createOffer(minter, nftAliceID, XRP(23)),
1994  txflags(tfSellNFToken));
1995  env.close();
1996  BEAST_EXPECT(ownerCount(env, alice) == 0);
1997  BEAST_EXPECT(ownerCount(env, becky) == 1);
1998  BEAST_EXPECT(ownerCount(env, minter) == 2);
1999 
2000  // alice buys back the nft.
2001  env(token::acceptSellOffer(alice, minterSellOfferIndex));
2002  env.close();
2003  BEAST_EXPECT(ownerCount(env, alice) == 1);
2004  BEAST_EXPECT(ownerCount(env, becky) == 1);
2005  BEAST_EXPECT(ownerCount(env, minter) == 0);
2006 
2007  // Remember the buy offer that becky made for alice's token way
2008  // back when? It's still in the ledger, and alice accepts it.
2009  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2010  env.close();
2011  BEAST_EXPECT(ownerCount(env, alice) == 0);
2012  BEAST_EXPECT(ownerCount(env, becky) == 1);
2013  BEAST_EXPECT(ownerCount(env, minter) == 0);
2014 
2015  // Just for tidyness, becky burns the token before shutting
2016  // things down.
2017  env(token::burn(becky, nftAliceID));
2018  env.close();
2019  BEAST_EXPECT(ownerCount(env, alice) == 0);
2020  BEAST_EXPECT(ownerCount(env, becky) == 0);
2021  BEAST_EXPECT(ownerCount(env, minter) == 0);
2022  }
2023  }
2024 
2025  void
2027  {
2028  // Exercise NFTs with and without a transferFee.
2029  testcase("Mint transferFee");
2030 
2031  using namespace test::jtx;
2032 
2033  Env env{*this, features};
2034 
2035  Account const alice{"alice"};
2036  Account const becky{"becky"};
2037  Account const carol{"carol"};
2038  Account const minter{"minter"};
2039  Account const gw{"gw"};
2040  IOU const gwXAU(gw["XAU"]);
2041 
2042  env.fund(XRP(1000), alice, becky, carol, minter, gw);
2043  env.close();
2044 
2045  env(trust(alice, gwXAU(2000)));
2046  env(trust(becky, gwXAU(2000)));
2047  env(trust(carol, gwXAU(2000)));
2048  env(trust(minter, gwXAU(2000)));
2049  env.close();
2050  env(pay(gw, alice, gwXAU(1000)));
2051  env(pay(gw, becky, gwXAU(1000)));
2052  env(pay(gw, carol, gwXAU(1000)));
2053  env(pay(gw, minter, gwXAU(1000)));
2054  env.close();
2055 
2056  // Giving alice a minter helps us see if transfer rates are affected
2057  // by that.
2058  env(token::setMinter(alice, minter));
2059  env.close();
2060 
2061  // If there is no transferFee, then alice gets nothing for the
2062  // transfer.
2063  {
2064  BEAST_EXPECT(ownerCount(env, alice) == 1);
2065  BEAST_EXPECT(ownerCount(env, becky) == 1);
2066  BEAST_EXPECT(ownerCount(env, carol) == 1);
2067  BEAST_EXPECT(ownerCount(env, minter) == 1);
2068 
2069  uint256 const nftID =
2070  token::getNextID(env, alice, 0u, tfTransferable);
2071  env(token::mint(alice), txflags(tfTransferable));
2072  env.close();
2073 
2074  // Becky buys the nft for XAU(10). Check balances.
2075  uint256 const beckyBuyOfferIndex =
2076  keylet::nftoffer(becky, env.seq(becky)).key;
2077  env(token::createOffer(becky, nftID, gwXAU(10)),
2078  token::owner(alice));
2079  env.close();
2080  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2081  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2082 
2083  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2084  env.close();
2085  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2086  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2087 
2088  // becky sells nft to carol. alice's balance should not change.
2089  uint256 const beckySellOfferIndex =
2090  keylet::nftoffer(becky, env.seq(becky)).key;
2091  env(token::createOffer(becky, nftID, gwXAU(10)),
2092  txflags(tfSellNFToken));
2093  env.close();
2094  env(token::acceptSellOffer(carol, beckySellOfferIndex));
2095  env.close();
2096  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2097  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2098  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2099 
2100  // minter buys nft from carol. alice's balance should not change.
2101  uint256 const minterBuyOfferIndex =
2102  keylet::nftoffer(minter, env.seq(minter)).key;
2103  env(token::createOffer(minter, nftID, gwXAU(10)),
2104  token::owner(carol));
2105  env.close();
2106  env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2107  env.close();
2108  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2109  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2110  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2111  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2112 
2113  // minter sells the nft to alice. gwXAU balances should finish
2114  // where they started.
2115  uint256 const minterSellOfferIndex =
2116  keylet::nftoffer(minter, env.seq(minter)).key;
2117  env(token::createOffer(minter, nftID, gwXAU(10)),
2118  txflags(tfSellNFToken));
2119  env.close();
2120  env(token::acceptSellOffer(alice, minterSellOfferIndex));
2121  env.close();
2122  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2123  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2124  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2125  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2126 
2127  // alice burns the nft to make later tests easier to think about.
2128  env(token::burn(alice, nftID));
2129  env.close();
2130  BEAST_EXPECT(ownerCount(env, alice) == 1);
2131  BEAST_EXPECT(ownerCount(env, becky) == 1);
2132  BEAST_EXPECT(ownerCount(env, carol) == 1);
2133  BEAST_EXPECT(ownerCount(env, minter) == 1);
2134  }
2135 
2136  // Set the smallest possible transfer fee.
2137  {
2138  // An nft with a transfer fee of 1 basis point.
2139  uint256 const nftID =
2140  token::getNextID(env, alice, 0u, tfTransferable, 1);
2141  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2142  env.close();
2143 
2144  // Becky buys the nft for XAU(10). Check balances.
2145  uint256 const beckyBuyOfferIndex =
2146  keylet::nftoffer(becky, env.seq(becky)).key;
2147  env(token::createOffer(becky, nftID, gwXAU(10)),
2148  token::owner(alice));
2149  env.close();
2150  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2151  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2152 
2153  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2154  env.close();
2155  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2156  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2157 
2158  // becky sells nft to carol. alice's balance goes up.
2159  uint256 const beckySellOfferIndex =
2160  keylet::nftoffer(becky, env.seq(becky)).key;
2161  env(token::createOffer(becky, nftID, gwXAU(10)),
2162  txflags(tfSellNFToken));
2163  env.close();
2164  env(token::acceptSellOffer(carol, beckySellOfferIndex));
2165  env.close();
2166 
2167  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2168  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2169  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2170 
2171  // minter buys nft from carol. alice's balance goes up.
2172  uint256 const minterBuyOfferIndex =
2173  keylet::nftoffer(minter, env.seq(minter)).key;
2174  env(token::createOffer(minter, nftID, gwXAU(10)),
2175  token::owner(carol));
2176  env.close();
2177  env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2178  env.close();
2179 
2180  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2181  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2182  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2183  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2184 
2185  // minter sells the nft to alice. Because alice is part of the
2186  // transaction no tranfer fee is removed.
2187  uint256 const minterSellOfferIndex =
2188  keylet::nftoffer(minter, env.seq(minter)).key;
2189  env(token::createOffer(minter, nftID, gwXAU(10)),
2190  txflags(tfSellNFToken));
2191  env.close();
2192  env(token::acceptSellOffer(alice, minterSellOfferIndex));
2193  env.close();
2194  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2195  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2196  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2197  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2198 
2199  // alice pays to becky and carol so subsequent tests are easier
2200  // to think about.
2201  env(pay(alice, becky, gwXAU(0.0001)));
2202  env(pay(alice, carol, gwXAU(0.0001)));
2203  env.close();
2204 
2205  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2206  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2207  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2208  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2209 
2210  // alice burns the nft to make later tests easier to think about.
2211  env(token::burn(alice, nftID));
2212  env.close();
2213  BEAST_EXPECT(ownerCount(env, alice) == 1);
2214  BEAST_EXPECT(ownerCount(env, becky) == 1);
2215  BEAST_EXPECT(ownerCount(env, carol) == 1);
2216  BEAST_EXPECT(ownerCount(env, minter) == 1);
2217  }
2218 
2219  // Set the largest allowed transfer fee.
2220  {
2221  // A transfer fee greater than 50% is not allowed.
2222  env(token::mint(alice),
2223  txflags(tfTransferable),
2224  token::xferFee(maxTransferFee + 1),
2226  env.close();
2227 
2228  // Make an nft with a transfer fee of 50%.
2229  uint256 const nftID = token::getNextID(
2230  env, alice, 0u, tfTransferable, maxTransferFee);
2231  env(token::mint(alice),
2232  txflags(tfTransferable),
2233  token::xferFee(maxTransferFee));
2234  env.close();
2235 
2236  // Becky buys the nft for XAU(10). Check balances.
2237  uint256 const beckyBuyOfferIndex =
2238  keylet::nftoffer(becky, env.seq(becky)).key;
2239  env(token::createOffer(becky, nftID, gwXAU(10)),
2240  token::owner(alice));
2241  env.close();
2242  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2243  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2244 
2245  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2246  env.close();
2247  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2248  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2249 
2250  // becky sells nft to minter. alice's balance goes up.
2251  uint256 const beckySellOfferIndex =
2252  keylet::nftoffer(becky, env.seq(becky)).key;
2253  env(token::createOffer(becky, nftID, gwXAU(100)),
2254  txflags(tfSellNFToken));
2255  env.close();
2256  env(token::acceptSellOffer(minter, beckySellOfferIndex));
2257  env.close();
2258 
2259  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2260  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2261  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2262 
2263  // carol buys nft from minter. alice's balance goes up.
2264  uint256 const carolBuyOfferIndex =
2265  keylet::nftoffer(carol, env.seq(carol)).key;
2266  env(token::createOffer(carol, nftID, gwXAU(10)),
2267  token::owner(minter));
2268  env.close();
2269  env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2270  env.close();
2271 
2272  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2273  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2274  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2275  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2276 
2277  // carol sells the nft to alice. Because alice is part of the
2278  // transaction no tranfer fee is removed.
2279  uint256 const carolSellOfferIndex =
2280  keylet::nftoffer(carol, env.seq(carol)).key;
2281  env(token::createOffer(carol, nftID, gwXAU(10)),
2282  txflags(tfSellNFToken));
2283  env.close();
2284  env(token::acceptSellOffer(alice, carolSellOfferIndex));
2285  env.close();
2286 
2287  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2288  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2289  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2290  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2291 
2292  // rebalance so subsequent tests are easier to think about.
2293  env(pay(alice, minter, gwXAU(55)));
2294  env(pay(becky, minter, gwXAU(40)));
2295  env.close();
2296  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2297  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2298  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2299  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2300 
2301  // alice burns the nft to make later tests easier to think about.
2302  env(token::burn(alice, nftID));
2303  env.close();
2304  BEAST_EXPECT(ownerCount(env, alice) == 1);
2305  BEAST_EXPECT(ownerCount(env, becky) == 1);
2306  BEAST_EXPECT(ownerCount(env, carol) == 1);
2307  BEAST_EXPECT(ownerCount(env, minter) == 1);
2308  }
2309 
2310  // See the impact of rounding when the nft is sold for small amounts
2311  // of drops.
2312  {
2313  // An nft with a transfer fee of 1 basis point.
2314  uint256 const nftID =
2315  token::getNextID(env, alice, 0u, tfTransferable, 1);
2316  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2317  env.close();
2318 
2319  // minter buys the nft for XRP(1). Since the transfer involves
2320  // alice there should be no transfer fee.
2321  STAmount fee = drops(10);
2322  STAmount aliceBalance = env.balance(alice);
2323  STAmount minterBalance = env.balance(minter);
2324  uint256 const minterBuyOfferIndex =
2325  keylet::nftoffer(minter, env.seq(minter)).key;
2326  env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2327  env.close();
2328  env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2329  env.close();
2330  aliceBalance += XRP(1) - fee;
2331  minterBalance -= XRP(1) + fee;
2332  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2333  BEAST_EXPECT(env.balance(minter) == minterBalance);
2334 
2335  // minter sells to carol. The payment is just small enough that
2336  // alice does not get any transfer fee.
2337  STAmount carolBalance = env.balance(carol);
2338  uint256 const minterSellOfferIndex =
2339  keylet::nftoffer(minter, env.seq(minter)).key;
2340  env(token::createOffer(minter, nftID, drops(99999)),
2341  txflags(tfSellNFToken));
2342  env.close();
2343  env(token::acceptSellOffer(carol, minterSellOfferIndex));
2344  env.close();
2345  minterBalance += drops(99999) - fee;
2346  carolBalance -= drops(99999) + fee;
2347  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2348  BEAST_EXPECT(env.balance(minter) == minterBalance);
2349  BEAST_EXPECT(env.balance(carol) == carolBalance);
2350 
2351  // carol sells to becky. This is the smallest amount to pay for a
2352  // transfer that enables a transfer fee of 1 basis point.
2353  STAmount beckyBalance = env.balance(becky);
2354  uint256 const beckyBuyOfferIndex =
2355  keylet::nftoffer(becky, env.seq(becky)).key;
2356  env(token::createOffer(becky, nftID, drops(100000)),
2357  token::owner(carol));
2358  env.close();
2359  env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2360  env.close();
2361  carolBalance += drops(99999) - fee;
2362  beckyBalance -= drops(100000) + fee;
2363  aliceBalance += drops(1);
2364 
2365  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2366  BEAST_EXPECT(env.balance(minter) == minterBalance);
2367  BEAST_EXPECT(env.balance(carol) == carolBalance);
2368  BEAST_EXPECT(env.balance(becky) == beckyBalance);
2369  }
2370 
2371  // See the impact of rounding when the nft is sold for small amounts
2372  // of an IOU.
2373  {
2374  // An nft with a transfer fee of 1 basis point.
2375  uint256 const nftID =
2376  token::getNextID(env, alice, 0u, tfTransferable, 1);
2377  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2378  env.close();
2379 
2380  // Due to the floating point nature of IOUs we need to
2381  // significantly reduce the gwXAU balances of our accounts prior
2382  // to the iou transfer. Otherwise no transfers will happen.
2383  env(pay(alice, gw, env.balance(alice, gwXAU)));
2384  env(pay(minter, gw, env.balance(minter, gwXAU)));
2385  env(pay(becky, gw, env.balance(becky, gwXAU)));
2386  env.close();
2387 
2388  STAmount const startXAUBalance(
2389  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2390  env(pay(gw, alice, startXAUBalance));
2391  env(pay(gw, minter, startXAUBalance));
2392  env(pay(gw, becky, startXAUBalance));
2393  env.close();
2394 
2395  // Here is the smallest expressible gwXAU amount.
2396  STAmount tinyXAU(
2397  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset);
2398 
2399  // minter buys the nft for tinyXAU. Since the transfer involves
2400  // alice there should be no transfer fee.
2401  STAmount aliceBalance = env.balance(alice, gwXAU);
2402  STAmount minterBalance = env.balance(minter, gwXAU);
2403  uint256 const minterBuyOfferIndex =
2404  keylet::nftoffer(minter, env.seq(minter)).key;
2405  env(token::createOffer(minter, nftID, tinyXAU),
2406  token::owner(alice));
2407  env.close();
2408  env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2409  env.close();
2410  aliceBalance += tinyXAU;
2411  minterBalance -= tinyXAU;
2412  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2413  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2414 
2415  // minter sells to carol.
2416  STAmount carolBalance = env.balance(carol, gwXAU);
2417  uint256 const minterSellOfferIndex =
2418  keylet::nftoffer(minter, env.seq(minter)).key;
2419  env(token::createOffer(minter, nftID, tinyXAU),
2420  txflags(tfSellNFToken));
2421  env.close();
2422  env(token::acceptSellOffer(carol, minterSellOfferIndex));
2423  env.close();
2424 
2425  minterBalance += tinyXAU;
2426  carolBalance -= tinyXAU;
2427  // tiny XAU is so small that alice does not get a transfer fee.
2428  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2429  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2430  BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2431 
2432  // carol sells to becky. This is the smallest gwXAU amount
2433  // to pay for a transfer that enables a transfer fee of 1.
2434  STAmount const cheapNFT(
2435  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2436 
2437  STAmount beckyBalance = env.balance(becky, gwXAU);
2438  uint256 const beckyBuyOfferIndex =
2439  keylet::nftoffer(becky, env.seq(becky)).key;
2440  env(token::createOffer(becky, nftID, cheapNFT),
2441  token::owner(carol));
2442  env.close();
2443  env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2444  env.close();
2445 
2446  aliceBalance += tinyXAU;
2447  beckyBalance -= cheapNFT;
2448  carolBalance += cheapNFT - tinyXAU;
2449  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2450  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2451  BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2452  BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2453  }
2454  }
2455 
2456  void
2458  {
2459  // Exercise the NFT taxon field.
2460  testcase("Mint taxon");
2461 
2462  using namespace test::jtx;
2463 
2464  Env env{*this, features};
2465 
2466  Account const alice{"alice"};
2467  Account const becky{"becky"};
2468 
2469  env.fund(XRP(1000), alice, becky);
2470  env.close();
2471 
2472  // The taxon field is incorporated straight into the NFT ID. So
2473  // tests only need to operate on NFT IDs; we don't need to generate
2474  // any transactions.
2475 
2476  // The taxon value should be recoverable from the NFT ID.
2477  {
2478  uint256 const nftID = token::getNextID(env, alice, 0u);
2479  BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon(0));
2480  }
2481 
2482  // Make sure the full range of taxon values work. We just tried
2483  // the minimum. Now try the largest.
2484  {
2485  uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2486  BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon((0xFFFFFFFF)));
2487  }
2488 
2489  // Do some touch testing to show that the taxon is recoverable no
2490  // matter what else changes around it in the nft ID.
2491  {
2492  std::uint32_t const taxon = rand_int<std::uint32_t>();
2493  for (int i = 0; i < 10; ++i)
2494  {
2495  // lambda to produce a useful message on error.
2496  auto check = [this](std::uint32_t taxon, uint256 const& nftID) {
2497  nft::Taxon const gotTaxon = nft::getTaxon(nftID);
2498  if (nft::toTaxon(taxon) == gotTaxon)
2499  pass();
2500  else
2501  {
2502  std::stringstream ss;
2503  ss << "Taxon recovery failed from nftID "
2504  << to_string(nftID) << ". Expected: " << taxon
2505  << "; got: " << gotTaxon;
2506  fail(ss.str());
2507  }
2508  };
2509 
2510  uint256 const nftAliceID = token::getID(
2511  alice,
2512  taxon,
2513  rand_int<std::uint32_t>(),
2514  rand_int<std::uint16_t>(),
2515  rand_int<std::uint16_t>());
2516  check(taxon, nftAliceID);
2517 
2518  uint256 const nftBeckyID = token::getID(
2519  becky,
2520  taxon,
2521  rand_int<std::uint32_t>(),
2522  rand_int<std::uint16_t>(),
2523  rand_int<std::uint16_t>());
2524  check(taxon, nftBeckyID);
2525  }
2526  }
2527  }
2528 
2529  void
2531  {
2532  // Exercise the NFT URI field.
2533  // 1. Create a number of NFTs with and without URIs.
2534  // 2. Retrieve the NFTs from the server.
2535  // 3. Make sure the right URI is attached to each NFT.
2536  testcase("Mint URI");
2537 
2538  using namespace test::jtx;
2539 
2540  Env env{*this, features};
2541 
2542  Account const alice{"alice"};
2543  Account const becky{"becky"};
2544 
2545  env.fund(XRP(10000), alice, becky);
2546  env.close();
2547 
2548  // lambda that returns a randomly generated string which fits
2549  // the constraints of a URI. Empty strings may be returned.
2550  // In the empty string case do not add the URI to the nft.
2551  auto randURI = []() {
2552  std::string ret;
2553 
2554  // About 20% of the returned strings should be empty
2555  if (rand_int(4) == 0)
2556  return ret;
2557 
2558  std::size_t const strLen = rand_int(256);
2559  ret.reserve(strLen);
2560  for (std::size_t i = 0; i < strLen; ++i)
2561  ret.push_back(rand_byte());
2562 
2563  return ret;
2564  };
2565 
2566  // Make a list of URIs that we'll put in nfts.
2567  struct Entry
2568  {
2569  std::string uri;
2570  std::uint32_t taxon;
2571 
2572  Entry(std::string uri_, std::uint32_t taxon_)
2573  : uri(std::move(uri_)), taxon(taxon_)
2574  {
2575  }
2576  };
2577 
2578  std::vector<Entry> entries;
2579  entries.reserve(100);
2580  for (std::size_t i = 0; i < 100; ++i)
2581  entries.emplace_back(randURI(), rand_int<std::uint32_t>());
2582 
2583  // alice creates nfts using entries.
2584  for (Entry const& entry : entries)
2585  {
2586  if (entry.uri.empty())
2587  {
2588  env(token::mint(alice, entry.taxon));
2589  }
2590  else
2591  {
2592  env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2593  }
2594  env.close();
2595  }
2596 
2597  // Recover alice's nfts from the ledger.
2598  Json::Value aliceNFTs = [&env, &alice]() {
2599  Json::Value params;
2600  params[jss::account] = alice.human();
2601  params[jss::type] = "state";
2602  return env.rpc("json", "account_nfts", to_string(params));
2603  }();
2604 
2605  // Verify that the returned NFTs match what we sent.
2606  Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2607  if (!BEAST_EXPECT(nfts.size() == entries.size()))
2608  return;
2609 
2610  // Sort the returned NFTs by nft_serial so the are in the same order
2611  // as entries.
2612  std::vector<Json::Value> sortedNFTs;
2613  sortedNFTs.reserve(nfts.size());
2614  for (std::size_t i = 0; i < nfts.size(); ++i)
2615  sortedNFTs.push_back(nfts[i]);
2616  std::sort(
2617  sortedNFTs.begin(),
2618  sortedNFTs.end(),
2619  [](Json::Value const& lhs, Json::Value const& rhs) {
2620  return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2621  });
2622 
2623  for (std::size_t i = 0; i < entries.size(); ++i)
2624  {
2625  Entry const& entry = entries[i];
2626  Json::Value const& ret = sortedNFTs[i];
2627  BEAST_EXPECT(entry.taxon == ret[sfNFTokenTaxon.jsonName]);
2628  if (entry.uri.empty())
2629  {
2630  BEAST_EXPECT(!ret.isMember(sfURI.jsonName));
2631  }
2632  else
2633  {
2634  BEAST_EXPECT(strHex(entry.uri) == ret[sfURI.jsonName]);
2635  }
2636  }
2637  }
2638 
2639  void
2641  {
2642  // Explore the CreateOffer Destination field.
2643  testcase("Create offer destination");
2644 
2645  using namespace test::jtx;
2646 
2647  Env env{*this, features};
2648 
2649  Account const issuer{"issuer"};
2650  Account const minter{"minter"};
2651  Account const buyer{"buyer"};
2652  Account const broker{"broker"};
2653 
2654  env.fund(XRP(1000), issuer, minter, buyer, broker);
2655 
2656  // We want to explore how issuers vs minters fits into the permission
2657  // scheme. So issuer issues and minter mints.
2658  env(token::setMinter(issuer, minter));
2659  env.close();
2660 
2661  uint256 const nftokenID =
2662  token::getNextID(env, issuer, 0, tfTransferable);
2663  env(token::mint(minter, 0),
2664  token::issuer(issuer),
2665  txflags(tfTransferable));
2666  env.close();
2667 
2668  // Test how adding a Destination field to an offer affects permissions
2669  // for cancelling offers.
2670  {
2671  uint256 const offerMinterToIssuer =
2672  keylet::nftoffer(minter, env.seq(minter)).key;
2673  env(token::createOffer(minter, nftokenID, drops(1)),
2674  token::destination(issuer),
2675  txflags(tfSellNFToken));
2676 
2677  uint256 const offerMinterToBuyer =
2678  keylet::nftoffer(minter, env.seq(minter)).key;
2679  env(token::createOffer(minter, nftokenID, drops(1)),
2680  token::destination(buyer),
2681  txflags(tfSellNFToken));
2682 
2683  // buy offers cannot contain a Destination, so this attempt fails.
2684  env(token::createOffer(issuer, nftokenID, drops(1)),
2685  token::owner(minter),
2686  token::destination(minter),
2687  ter(temMALFORMED));
2688 
2689  env.close();
2690  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2691  BEAST_EXPECT(ownerCount(env, minter) == 3);
2692  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2693 
2694  // Test who gets to cancel the offers. Anyone outside of the
2695  // offer-owner/destination pair should not be able to cancel the
2696  // offers.
2697  //
2698  // Note that issuer does not have any special permissions regarding
2699  // offer cancellation. issuer cannot cancel an offer for an
2700  // NFToken they issued.
2701  env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2702  ter(tecNO_PERMISSION));
2703  env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2704  ter(tecNO_PERMISSION));
2705  env.close();
2706  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2707  BEAST_EXPECT(ownerCount(env, minter) == 3);
2708  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2709 
2710  // Both the offer creator and and destination should be able to
2711  // cancel the offers.
2712  env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2713  env(token::cancelOffer(minter, {offerMinterToIssuer}));
2714  env.close();
2715  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2716  BEAST_EXPECT(ownerCount(env, minter) == 1);
2717  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2718  }
2719 
2720  // Test how adding a Destination field to a sell offer affects
2721  // accepting that offer.
2722  {
2723  uint256 const offerMinterToBuyer =
2724  keylet::nftoffer(minter, env.seq(minter)).key;
2725  env(token::createOffer(minter, nftokenID, drops(1)),
2726  token::destination(buyer),
2727  txflags(tfSellNFToken));
2728  env.close();
2729  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2730  BEAST_EXPECT(ownerCount(env, minter) == 2);
2731  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2732 
2733  // issuer cannot accept a sell offer where they are not the
2734  // destination.
2735  env(token::acceptSellOffer(issuer, offerMinterToBuyer),
2736  ter(tecNO_PERMISSION));
2737  env.close();
2738  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2739  BEAST_EXPECT(ownerCount(env, minter) == 2);
2740  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2741 
2742  // However buyer can accept the sell offer.
2743  env(token::acceptSellOffer(buyer, offerMinterToBuyer));
2744  env.close();
2745  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2746  BEAST_EXPECT(ownerCount(env, minter) == 0);
2747  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2748  }
2749 
2750  // You can't add a Destination field to a buy offer.
2751  {
2752  env(token::createOffer(minter, nftokenID, drops(1)),
2753  token::owner(buyer),
2754  token::destination(buyer),
2755  ter(temMALFORMED));
2756  env.close();
2757  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2758  BEAST_EXPECT(ownerCount(env, minter) == 0);
2759  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2760 
2761  // However without the Destination the buy offer works fine.
2762  uint256 const offerMinterToBuyer =
2763  keylet::nftoffer(minter, env.seq(minter)).key;
2764  env(token::createOffer(minter, nftokenID, drops(1)),
2765  token::owner(buyer));
2766  env.close();
2767  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2768  BEAST_EXPECT(ownerCount(env, minter) == 1);
2769  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2770 
2771  // Buyer accepts minter's offer.
2772  env(token::acceptBuyOffer(buyer, offerMinterToBuyer));
2773  env.close();
2774  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2775  BEAST_EXPECT(ownerCount(env, minter) == 1);
2776  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2777  }
2778 
2779  // Show that a sell offer's Destination can broker that sell offer
2780  // to another account.
2781  {
2782  uint256 const offerMinterToBroker =
2783  keylet::nftoffer(minter, env.seq(minter)).key;
2784  env(token::createOffer(minter, nftokenID, drops(1)),
2785  token::destination(broker),
2786  txflags(tfSellNFToken));
2787 
2788  uint256 const offerBuyerToMinter =
2789  keylet::nftoffer(buyer, env.seq(buyer)).key;
2790  env(token::createOffer(buyer, nftokenID, drops(1)),
2791  token::owner(minter));
2792 
2793  env.close();
2794  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2795  BEAST_EXPECT(ownerCount(env, minter) == 2);
2796  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2797 
2798  // issuer cannot broker the offers, because they are not the
2799  // Destination.
2800  env(token::brokerOffers(
2801  issuer, offerBuyerToMinter, offerMinterToBroker),
2803  env.close();
2804  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2805  BEAST_EXPECT(ownerCount(env, minter) == 2);
2806  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2807 
2808  // Since broker is the sell offer's destination, they can broker
2809  // the two offers.
2810  env(token::brokerOffers(
2811  broker, offerBuyerToMinter, offerMinterToBroker));
2812  env.close();
2813  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2814  BEAST_EXPECT(ownerCount(env, minter) == 0);
2815  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2816  }
2817 
2818  // Show that brokered mode cannot complete a transfer where the
2819  // Destination doesn't match, but can complete if the Destination
2820  // does match.
2821  {
2822  uint256 const offerBuyerToMinter =
2823  keylet::nftoffer(buyer, env.seq(buyer)).key;
2824  env(token::createOffer(buyer, nftokenID, drops(1)),
2825  token::destination(minter),
2826  txflags(tfSellNFToken));
2827 
2828  uint256 const offerMinterToBuyer =
2829  keylet::nftoffer(minter, env.seq(minter)).key;
2830  env(token::createOffer(minter, nftokenID, drops(1)),
2831  token::owner(buyer));
2832 
2833  uint256 const offerIssuerToBuyer =
2834  keylet::nftoffer(issuer, env.seq(issuer)).key;
2835  env(token::createOffer(issuer, nftokenID, drops(1)),
2836  token::owner(buyer));
2837 
2838  env.close();
2839  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2840  BEAST_EXPECT(ownerCount(env, minter) == 1);
2841  BEAST_EXPECT(ownerCount(env, buyer) == 2);
2842 
2843  // Cannot broker offers when the sell destination is not the buyer.
2844  env(token::brokerOffers(
2845  broker, offerIssuerToBuyer, offerBuyerToMinter),
2847  env.close();
2848  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2849  BEAST_EXPECT(ownerCount(env, minter) == 1);
2850  BEAST_EXPECT(ownerCount(env, buyer) == 2);
2851 
2852  // Broker is successful when destination is buyer.
2853  env(token::brokerOffers(
2854  broker, offerMinterToBuyer, offerBuyerToMinter));
2855  env.close();
2856  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2857  BEAST_EXPECT(ownerCount(env, minter) == 1);
2858  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2859  }
2860  }
2861 
2862  void
2864  {
2865  // Explore the CreateOffer Expiration field.
2866  testcase("Create offer expiration");
2867 
2868  using namespace test::jtx;
2869 
2870  Env env{*this, features};
2871 
2872  Account const issuer{"issuer"};
2873  Account const minter{"minter"};
2874  Account const buyer{"buyer"};
2875 
2876  env.fund(XRP(1000), issuer, minter, buyer);
2877 
2878  // We want to explore how issuers vs minters fits into the permission
2879  // scheme. So issuer issues and minter mints.
2880  env(token::setMinter(issuer, minter));
2881  env.close();
2882 
2883  uint256 const nftokenID0 =
2884  token::getNextID(env, issuer, 0, tfTransferable);
2885  env(token::mint(minter, 0),
2886  token::issuer(issuer),
2887  txflags(tfTransferable));
2888  env.close();
2889 
2890  uint256 const nftokenID1 =
2891  token::getNextID(env, issuer, 0, tfTransferable);
2892  env(token::mint(minter, 0),
2893  token::issuer(issuer),
2894  txflags(tfTransferable));
2895  env.close();
2896 
2897  // Test how adding an Expiration field to an offer affects permissions
2898  // for cancelling offers.
2899  {
2900  std::uint32_t const expiration = lastClose(env) + 25;
2901 
2902  uint256 const offerMinterToIssuer =
2903  keylet::nftoffer(minter, env.seq(minter)).key;
2904  env(token::createOffer(minter, nftokenID0, drops(1)),
2905  token::destination(issuer),
2906  token::expiration(expiration),
2907  txflags(tfSellNFToken));
2908 
2909  uint256 const offerMinterToAnyone =
2910  keylet::nftoffer(minter, env.seq(minter)).key;
2911  env(token::createOffer(minter, nftokenID0, drops(1)),
2912  token::expiration(expiration),
2913  txflags(tfSellNFToken));
2914 
2915  uint256 const offerIssuerToMinter =
2916  keylet::nftoffer(issuer, env.seq(issuer)).key;
2917  env(token::createOffer(issuer, nftokenID0, drops(1)),
2918  token::owner(minter),
2919  token::expiration(expiration));
2920 
2921  uint256 const offerBuyerToMinter =
2922  keylet::nftoffer(buyer, env.seq(buyer)).key;
2923  env(token::createOffer(buyer, nftokenID0, drops(1)),
2924  token::owner(minter),
2925  token::expiration(expiration));
2926  env.close();
2927  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2928  BEAST_EXPECT(ownerCount(env, minter) == 3);
2929  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2930 
2931  // Test who gets to cancel the offers. Anyone outside of the
2932  // offer-owner/destination pair should not be able to cancel
2933  // unexpired offers.
2934  //
2935  // Note that these are tec responses, so these transactions will
2936  // not be retried by the ledger.
2937  env(token::cancelOffer(issuer, {offerMinterToAnyone}),
2938  ter(tecNO_PERMISSION));
2939  env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2940  ter(tecNO_PERMISSION));
2941  env.close();
2942  BEAST_EXPECT(lastClose(env) < expiration);
2943  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2944  BEAST_EXPECT(ownerCount(env, minter) == 3);
2945  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2946 
2947  // The offer creator can cancel their own unexpired offer.
2948  env(token::cancelOffer(minter, {offerMinterToAnyone}));
2949 
2950  // The destination of a sell offer can cancel the NFT owner's
2951  // unexpired offer.
2952  env(token::cancelOffer(issuer, {offerMinterToIssuer}));
2953 
2954  // Close enough ledgers to get past the expiration.
2955  while (lastClose(env) < expiration)
2956  env.close();
2957 
2958  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2959  BEAST_EXPECT(ownerCount(env, minter) == 1);
2960  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2961 
2962  // Anyone can cancel expired offers.
2963  env(token::cancelOffer(issuer, {offerBuyerToMinter}));
2964  env(token::cancelOffer(buyer, {offerIssuerToMinter}));
2965  env.close();
2966  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2967  BEAST_EXPECT(ownerCount(env, minter) == 1);
2968  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2969  }
2970  // Show that:
2971  // 1. An unexpired sell offer with an expiration can be accepted.
2972  // 2. An expired sell offer cannot be accepted and remains
2973  // in ledger after the accept fails.
2974  {
2975  std::uint32_t const expiration = lastClose(env) + 25;
2976 
2977  uint256 const offer0 =
2978  keylet::nftoffer(minter, env.seq(minter)).key;
2979  env(token::createOffer(minter, nftokenID0, drops(1)),
2980  token::expiration(expiration),
2981  txflags(tfSellNFToken));
2982 
2983  uint256 const offer1 =
2984  keylet::nftoffer(minter, env.seq(minter)).key;
2985  env(token::createOffer(minter, nftokenID1, drops(1)),
2986  token::expiration(expiration),
2987  txflags(tfSellNFToken));
2988  env.close();
2989  BEAST_EXPECT(lastClose(env) < expiration);
2990  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2991  BEAST_EXPECT(ownerCount(env, minter) == 3);
2992  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2993 
2994  // Anyone can accept an unexpired sell offer.
2995  env(token::acceptSellOffer(buyer, offer0));
2996 
2997  // Close enough ledgers to get past the expiration.
2998  while (lastClose(env) < expiration)
2999  env.close();
3000 
3001  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3002  BEAST_EXPECT(ownerCount(env, minter) == 2);
3003  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3004 
3005  // No one can accept an expired sell offer.
3006  env(token::acceptSellOffer(buyer, offer1), ter(tecEXPIRED));
3007  env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
3008  env.close();
3009 
3010  // The expired sell offer is still in the ledger.
3011  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3012  BEAST_EXPECT(ownerCount(env, minter) == 2);
3013  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3014 
3015  // Anyone can cancel the expired sell offer.
3016  env(token::cancelOffer(issuer, {offer1}));
3017  env.close();
3018  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3019  BEAST_EXPECT(ownerCount(env, minter) == 1);
3020  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3021 
3022  // Transfer nftokenID0 back to minter so we start the next test in
3023  // a simple place.
3024  uint256 const offerSellBack =
3025  keylet::nftoffer(buyer, env.seq(buyer)).key;
3026  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3027  txflags(tfSellNFToken),
3028  token::destination(minter));
3029  env.close();
3030  env(token::acceptSellOffer(minter, offerSellBack));
3031  env.close();
3032  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3033  BEAST_EXPECT(ownerCount(env, minter) == 1);
3034  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3035  }
3036  // Show that:
3037  // 1. An unexpired buy offer with an expiration can be accepted.
3038  // 2. An expired buy offer cannot be accepted and remains
3039  // in ledger after the accept fails.
3040  {
3041  std::uint32_t const expiration = lastClose(env) + 25;
3042 
3043  uint256 const offer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3044  env(token::createOffer(buyer, nftokenID0, drops(1)),
3045  token::owner(minter),
3046  token::expiration(expiration));
3047 
3048  uint256 const offer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3049  env(token::createOffer(buyer, nftokenID1, drops(1)),
3050  token::owner(minter),
3051  token::expiration(expiration));
3052  env.close();
3053  BEAST_EXPECT(lastClose(env) < expiration);
3054  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3055  BEAST_EXPECT(ownerCount(env, minter) == 1);
3056  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3057 
3058  // An unexpired buy offer can be accepted.
3059  env(token::acceptBuyOffer(minter, offer0));
3060 
3061  // Close enough ledgers to get past the expiration.
3062  while (lastClose(env) < expiration)
3063  env.close();
3064 
3065  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3066  BEAST_EXPECT(ownerCount(env, minter) == 1);
3067  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3068 
3069  // An expired buy offer cannot be accepted.
3070  env(token::acceptBuyOffer(minter, offer1), ter(tecEXPIRED));
3071  env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
3072  env.close();
3073 
3074  // The expired buy offer is still in the ledger.
3075  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3076  BEAST_EXPECT(ownerCount(env, minter) == 1);
3077  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3078 
3079  // Anyone can cancel the expired buy offer.
3080  env(token::cancelOffer(issuer, {offer1}));
3081  env.close();
3082  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3083  BEAST_EXPECT(ownerCount(env, minter) == 1);
3084  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3085 
3086  // Transfer nftokenID0 back to minter so we start the next test in
3087  // a simple place.
3088  uint256 const offerSellBack =
3089  keylet::nftoffer(buyer, env.seq(buyer)).key;
3090  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3091  txflags(tfSellNFToken),
3092  token::destination(minter));
3093  env.close();
3094  env(token::acceptSellOffer(minter, offerSellBack));
3095  env.close();
3096  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3097  BEAST_EXPECT(ownerCount(env, minter) == 1);
3098  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3099  }
3100  // Show that in brokered mode:
3101  // 1. An unexpired sell offer with an expiration can be accepted.
3102  // 2. An expired sell offer cannot be accepted and remains
3103  // in ledger after the accept fails.
3104  {
3105  std::uint32_t const expiration = lastClose(env) + 25;
3106 
3107  uint256 const sellOffer0 =
3108  keylet::nftoffer(minter, env.seq(minter)).key;
3109  env(token::createOffer(minter, nftokenID0, drops(1)),
3110  token::expiration(expiration),
3111  txflags(tfSellNFToken));
3112 
3113  uint256 const sellOffer1 =
3114  keylet::nftoffer(minter, env.seq(minter)).key;
3115  env(token::createOffer(minter, nftokenID1, drops(1)),
3116  token::expiration(expiration),
3117  txflags(tfSellNFToken));
3118 
3119  uint256 const buyOffer0 =
3120  keylet::nftoffer(buyer, env.seq(buyer)).key;
3121  env(token::createOffer(buyer, nftokenID0, drops(1)),
3122  token::owner(minter));
3123 
3124  uint256 const buyOffer1 =
3125  keylet::nftoffer(buyer, env.seq(buyer)).key;
3126  env(token::createOffer(buyer, nftokenID1, drops(1)),
3127  token::owner(minter));
3128 
3129  env.close();
3130  BEAST_EXPECT(lastClose(env) < expiration);
3131  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3132  BEAST_EXPECT(ownerCount(env, minter) == 3);
3133  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3134 
3135  // An unexpired offer can be brokered.
3136  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3137 
3138  // Close enough ledgers to get past the expiration.
3139  while (lastClose(env) < expiration)
3140  env.close();
3141 
3142  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3143  BEAST_EXPECT(ownerCount(env, minter) == 2);
3144  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3145 
3146  // If the sell offer is expired it cannot be brokered.
3147  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3148  ter(tecEXPIRED));
3149  env.close();
3150 
3151  // The expired sell offer is still in the ledger.
3152  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3153  BEAST_EXPECT(ownerCount(env, minter) == 2);
3154  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3155 
3156  // Anyone can cancel the expired sell offer.
3157  env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3158  env.close();
3159  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3160  BEAST_EXPECT(ownerCount(env, minter) == 1);
3161  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3162 
3163  // Transfer nftokenID0 back to minter so we start the next test in
3164  // a simple place.
3165  uint256 const offerSellBack =
3166  keylet::nftoffer(buyer, env.seq(buyer)).key;
3167  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3168  txflags(tfSellNFToken),
3169  token::destination(minter));
3170  env.close();
3171  env(token::acceptSellOffer(minter, offerSellBack));
3172  env.close();
3173  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3174  BEAST_EXPECT(ownerCount(env, minter) == 1);
3175  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3176  }
3177  // Show that in brokered mode:
3178  // 1. An unexpired buy offer with an expiration can be accepted.
3179  // 2. An expired buy offer cannot be accepted and remains
3180  // in ledger after the accept fails.
3181  {
3182  std::uint32_t const expiration = lastClose(env) + 25;
3183 
3184  uint256 const sellOffer0 =
3185  keylet::nftoffer(minter, env.seq(minter)).key;
3186  env(token::createOffer(minter, nftokenID0, drops(1)),
3187  txflags(tfSellNFToken));
3188 
3189  uint256 const sellOffer1 =
3190  keylet::nftoffer(minter, env.seq(minter)).key;
3191  env(token::createOffer(minter, nftokenID1, drops(1)),
3192  txflags(tfSellNFToken));
3193 
3194  uint256 const buyOffer0 =
3195  keylet::nftoffer(buyer, env.seq(buyer)).key;
3196  env(token::createOffer(buyer, nftokenID0, drops(1)),
3197  token::expiration(expiration),
3198  token::owner(minter));
3199 
3200  uint256 const buyOffer1 =
3201  keylet::nftoffer(buyer, env.seq(buyer)).key;
3202  env(token::createOffer(buyer, nftokenID1, drops(1)),
3203  token::expiration(expiration),
3204  token::owner(minter));
3205 
3206  env.close();
3207  BEAST_EXPECT(lastClose(env) < expiration);
3208  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3209  BEAST_EXPECT(ownerCount(env, minter) == 3);
3210  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3211 
3212  // An unexpired offer can be brokered.
3213  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3214 
3215  // Close enough ledgers to get past the expiration.
3216  while (lastClose(env) < expiration)
3217  env.close();
3218 
3219  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3220  BEAST_EXPECT(ownerCount(env, minter) == 2);
3221  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3222 
3223  // If the buy offer is expired it cannot be brokered.
3224  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3225  ter(tecEXPIRED));
3226  env.close();
3227 
3228  // The expired buy offer is still in the ledger.
3229  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3230  BEAST_EXPECT(ownerCount(env, minter) == 2);
3231  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3232 
3233  // Anyone can cancel the expired buy offer.
3234  env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3235  env.close();
3236  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3237  BEAST_EXPECT(ownerCount(env, minter) == 1);
3238  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3239 
3240  // Transfer nftokenID0 back to minter so we start the next test in
3241  // a simple place.
3242  uint256 const offerSellBack =
3243  keylet::nftoffer(buyer, env.seq(buyer)).key;
3244  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3245  txflags(tfSellNFToken),
3246  token::destination(minter));
3247  env.close();
3248  env(token::acceptSellOffer(minter, offerSellBack));
3249  env.close();
3250  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3251  BEAST_EXPECT(ownerCount(env, minter) == 1);
3252  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3253  }
3254  // Show that in brokered mode:
3255  // 1. An unexpired buy/sell offer pair with an expiration can be
3256  // accepted.
3257  // 2. An expired buy/sell offer pair cannot be accepted and they
3258  // remain in ledger after the accept fails.
3259  {
3260  std::uint32_t const expiration = lastClose(env) + 25;
3261 
3262  uint256 const sellOffer0 =
3263  keylet::nftoffer(minter, env.seq(minter)).key;
3264  env(token::createOffer(minter, nftokenID0, drops(1)),
3265  token::expiration(expiration),
3266  txflags(tfSellNFToken));
3267 
3268  uint256 const sellOffer1 =
3269  keylet::nftoffer(minter, env.seq(minter)).key;
3270  env(token::createOffer(minter, nftokenID1, drops(1)),
3271  token::expiration(expiration),
3272  txflags(tfSellNFToken));
3273 
3274  uint256 const buyOffer0 =
3275  keylet::nftoffer(buyer, env.seq(buyer)).key;
3276  env(token::createOffer(buyer, nftokenID0, drops(1)),
3277  token::expiration(expiration),
3278  token::owner(minter));
3279 
3280  uint256 const buyOffer1 =
3281  keylet::nftoffer(buyer, env.seq(buyer)).key;
3282  env(token::createOffer(buyer, nftokenID1, drops(1)),
3283  token::expiration(expiration),
3284  token::owner(minter));
3285 
3286  env.close();
3287  BEAST_EXPECT(lastClose(env) < expiration);
3288  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3289  BEAST_EXPECT(ownerCount(env, minter) == 3);
3290  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3291 
3292  // Unexpired offers can be brokered.
3293  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3294 
3295  // Close enough ledgers to get past the expiration.
3296  while (lastClose(env) < expiration)
3297  env.close();
3298 
3299  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3300  BEAST_EXPECT(ownerCount(env, minter) == 2);
3301  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3302 
3303  // If the offers are expired they cannot be brokered.
3304  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3305  ter(tecEXPIRED));
3306  env.close();
3307 
3308  // The expired offers are still in the ledger.
3309  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3310  BEAST_EXPECT(ownerCount(env, minter) == 2);
3311  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3312 
3313  // Anyone can cancel the expired offers.
3314  env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3315  env.close();
3316  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3317  BEAST_EXPECT(ownerCount(env, minter) == 1);
3318  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3319 
3320  // Transfer nftokenID0 back to minter so we start the next test in
3321  // a simple place.
3322  uint256 const offerSellBack =
3323  keylet::nftoffer(buyer, env.seq(buyer)).key;
3324  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3325  txflags(tfSellNFToken),
3326  token::destination(minter));
3327  env.close();
3328  env(token::acceptSellOffer(minter, offerSellBack));
3329  env.close();
3330  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3331  BEAST_EXPECT(ownerCount(env, minter) == 1);
3332  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3333  }
3334  }
3335 
3336  void
3338  {
3339  // Look at offer canceling.
3340  testcase("Cancel offers");
3341 
3342  using namespace test::jtx;
3343 
3344  Env env{*this, features};
3345 
3346  Account const alice("alice");
3347  Account const becky("becky");
3348  Account const minter("minter");
3349  env.fund(XRP(50000), alice, becky, minter);
3350  env.close();
3351 
3352  // alice has a minter to see if minters have offer canceling permission.
3353  env(token::setMinter(alice, minter));
3354  env.close();
3355 
3356  uint256 const nftokenID =
3357  token::getNextID(env, alice, 0, tfTransferable);
3358  env(token::mint(alice, 0), txflags(tfTransferable));
3359  env.close();
3360 
3361  // Anyone can cancel an expired offer.
3362  uint256 const expiredOfferIndex =
3363  keylet::nftoffer(alice, env.seq(alice)).key;
3364 
3365  env(token::createOffer(alice, nftokenID, XRP(1000)),
3366  txflags(tfSellNFToken),
3367  token::expiration(lastClose(env) + 13));
3368  env.close();
3369 
3370  // The offer has not expired yet, so becky can't cancel it now.
3371  BEAST_EXPECT(ownerCount(env, alice) == 2);
3372  env(token::cancelOffer(becky, {expiredOfferIndex}),
3373  ter(tecNO_PERMISSION));
3374  env.close();
3375 
3376  // Close a couple of ledgers and advance the time. Then becky
3377  // should be able to cancel the (now) expired offer.
3378  env.close();
3379  env.close();
3380  env(token::cancelOffer(becky, {expiredOfferIndex}));
3381  env.close();
3382  BEAST_EXPECT(ownerCount(env, alice) == 1);
3383 
3384  // Create a couple of offers with a destination. Those offers
3385  // should be cancellable by the creator and the destination.
3386  uint256 const dest1OfferIndex =
3387  keylet::nftoffer(alice, env.seq(alice)).key;
3388 
3389  env(token::createOffer(alice, nftokenID, XRP(1000)),
3390  token::destination(becky),
3391  txflags(tfSellNFToken));
3392  env.close();
3393  BEAST_EXPECT(ownerCount(env, alice) == 2);
3394 
3395  // Minter can't cancel that offer, but becky (the destination) can.
3396  env(token::cancelOffer(minter, {dest1OfferIndex}),
3397  ter(tecNO_PERMISSION));
3398  env.close();
3399  BEAST_EXPECT(ownerCount(env, alice) == 2);
3400 
3401  env(token::cancelOffer(becky, {dest1OfferIndex}));
3402  env.close();
3403  BEAST_EXPECT(ownerCount(env, alice) == 1);
3404 
3405  // alice can cancel her own offer, even if becky is the destination.
3406  uint256 const dest2OfferIndex =
3407  keylet::nftoffer(alice, env.seq(alice)).key;
3408 
3409  env(token::createOffer(alice, nftokenID, XRP(1000)),
3410  token::destination(becky),
3411  txflags(tfSellNFToken));
3412  env.close();
3413  BEAST_EXPECT(ownerCount(env, alice) == 2);
3414 
3415  env(token::cancelOffer(alice, {dest2OfferIndex}));
3416  env.close();
3417  BEAST_EXPECT(ownerCount(env, alice) == 1);
3418 
3419  // The issuer has no special permissions regarding offer cancellation.
3420  // Minter creates a token with alice as issuer. alice cannot cancel
3421  // minter's offer.
3422  uint256 const mintersNFTokenID =
3423  token::getNextID(env, alice, 0, tfTransferable);
3424  env(token::mint(minter, 0),
3425  token::issuer(alice),
3426  txflags(tfTransferable));
3427  env.close();
3428 
3429  uint256 const minterOfferIndex =
3430  keylet::nftoffer(minter, env.seq(minter)).key;
3431 
3432  env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3433  txflags(tfSellNFToken));
3434  env.close();
3435  BEAST_EXPECT(ownerCount(env, minter) == 2);
3436 
3437  // Nobody other than minter should be able to cancel minter's offer.
3438  env(token::cancelOffer(alice, {minterOfferIndex}),
3439  ter(tecNO_PERMISSION));
3440  env(token::cancelOffer(becky, {minterOfferIndex}),
3441  ter(tecNO_PERMISSION));
3442  env.close();
3443  BEAST_EXPECT(ownerCount(env, minter) == 2);
3444 
3445  env(token::cancelOffer(minter, {minterOfferIndex}));
3446  env.close();
3447  BEAST_EXPECT(ownerCount(env, minter) == 1);
3448  }
3449 
3450  void
3452  {
3453  // Look at the case where too many offers are passed in a cancel.
3454  testcase("Cancel too many offers");
3455 
3456  using namespace test::jtx;
3457 
3458  Env env{*this, features};
3459 
3460  // We want to maximize the metadata from a cancel offer transaction to
3461  // make sure we don't hit metadata limits. The way we'll do that is:
3462  //
3463  // 1. Generate twice as many separate funded accounts as we have
3464  // offers.
3465  // 2.
3466  // a. One of these accounts mints an NFT with a full URL.
3467  // b. The other account makes an offer that will expire soon.
3468  // 3. After all of these offers have expired, cancel all of the
3469  // expired offers in a single transaction.
3470  //
3471  // I can't think of any way to increase the metadata beyond this,
3472  // but I'm open to ideas.
3473  Account const alice("alice");
3474  env.fund(XRP(1000), alice);
3475  env.close();
3476 
3477  std::string const uri(maxTokenURILength, '?');
3478  std::vector<uint256> offerIndexes;
3479  offerIndexes.reserve(maxTokenOfferCancelCount + 1);
3480  for (uint32_t i = 0; i < maxTokenOfferCancelCount + 1; ++i)
3481  {
3482  Account const nftAcct(std::string("nftAcct") + std::to_string(i));
3483  Account const offerAcct(
3484  std::string("offerAcct") + std::to_string(i));
3485  env.fund(XRP(1000), nftAcct, offerAcct);
3486  env.close();
3487 
3488  uint256 const nftokenID =
3489  token::getNextID(env, nftAcct, 0, tfTransferable);
3490  env(token::mint(nftAcct, 0),
3491  token::uri(uri),
3492  txflags(tfTransferable));
3493  env.close();
3494 
3495  offerIndexes.push_back(
3496  keylet::nftoffer(offerAcct, env.seq(offerAcct)).key);
3497  env(token::createOffer(offerAcct, nftokenID, drops(1)),
3498  token::owner(nftAcct),
3499  token::expiration(lastClose(env) + 5));
3500  env.close();
3501  }
3502 
3503  // Close the ledger so the last of the offers expire.
3504  env.close();
3505 
3506  // All offers should be in the ledger.
3507  for (uint256 const& offerIndex : offerIndexes)
3508  {
3509  BEAST_EXPECT(env.le(keylet::nftoffer(offerIndex)));
3510  }
3511 
3512  // alice attempts to cancel all of the expired offers. There is one
3513  // too many so the request fails.
3514  env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3515  env.close();
3516 
3517  // However alice can cancel just one of the offers.
3518  env(token::cancelOffer(alice, {offerIndexes.back()}));
3519  env.close();
3520 
3521  // Verify that offer is gone from the ledger.
3522  BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndexes.back())));
3523  offerIndexes.pop_back();
3524 
3525  // But alice adds a sell offer to the list...
3526  {
3527  uint256 const nftokenID =
3528  token::getNextID(env, alice, 0, tfTransferable);
3529  env(token::mint(alice, 0),
3530  token::uri(uri),
3531  txflags(tfTransferable));
3532  env.close();
3533 
3534  offerIndexes.push_back(keylet::nftoffer(alice, env.seq(alice)).key);
3535  env(token::createOffer(alice, nftokenID, drops(1)),
3536  txflags(tfSellNFToken));
3537  env.close();
3538 
3539  // alice's owner count should now to 2 for the nft and the offer.
3540  BEAST_EXPECT(ownerCount(env, alice) == 2);
3541 
3542  // Because alice added the sell offer there are still too many
3543  // offers in the list to cancel.
3544  env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3545  env.close();
3546 
3547  // alice burns her nft which removes the nft and the offer.
3548  env(token::burn(alice, nftokenID));
3549  env.close();
3550 
3551  // If alice's owner count is zero we can see that the offer
3552  // and nft are both gone.
3553  BEAST_EXPECT(ownerCount(env, alice) == 0);
3554  offerIndexes.pop_back();
3555  }
3556 
3557  // Now there are few enough offers in the list that they can all
3558  // be cancelled in a single transaction.
3559  env(token::cancelOffer(alice, offerIndexes));
3560  env.close();
3561 
3562  // Verify that remaining offers are gone from the ledger.
3563  for (uint256 const& offerIndex : offerIndexes)
3564  {
3565  BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndex)));
3566  }
3567  }
3568 
3569  void
3571  {
3572  // Look at the case where too many offers are passed in a cancel.
3573  testcase("Brokered NFT offer accept");
3574 
3575  using namespace test::jtx;
3576 
3577  Env env{*this, features};
3578 
3579  // The most important thing to explore here is the way funds are
3580  // assigned from the buyer to...
3581  // o the Seller,
3582  // o the Broker, and
3583  // o the Issuer (in the case of a transfer fee).
3584 
3585  Account const issuer{"issuer"};
3586  Account const minter{"minter"};
3587  Account const buyer{"buyer"};
3588  Account const broker{"broker"};
3589  Account const gw{"gw"};
3590  IOU const gwXAU(gw["XAU"]);
3591 
3592  env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3593  env.close();
3594 
3595  env(trust(issuer, gwXAU(2000)));
3596  env(trust(minter, gwXAU(2000)));
3597  env(trust(buyer, gwXAU(2000)));
3598  env(trust(broker, gwXAU(2000)));
3599  env.close();
3600 
3601  env(token::setMinter(issuer, minter));
3602  env.close();
3603 
3604  // Lambda to check owner count of all accounts is one.
3605  auto checkOwnerCountIsOne =
3606  [this, &env](
3608  accounts,
3609  int line) {
3610  for (Account const& acct : accounts)
3611  {
3612  if (std::uint32_t ownerCount = this->ownerCount(env, acct);
3613  ownerCount != 1)
3614  {
3615  std::stringstream ss;
3616  ss << "Account " << acct.human()
3617  << " expected ownerCount == 1. Got " << ownerCount;
3618  fail(ss.str(), __FILE__, line);
3619  }
3620  }
3621  };
3622 
3623  // Lambda that mints an NFT and returns the nftID.
3624  auto mintNFT = [&env, &issuer, &minter](std::uint16_t xferFee = 0) {
3625  uint256 const nftID =
3626  token::getNextID(env, issuer, 0, tfTransferable, xferFee);
3627  env(token::mint(minter, 0),
3628  token::issuer(issuer),
3629  token::xferFee(xferFee),
3630  txflags(tfTransferable));
3631  env.close();
3632  return nftID;
3633  };
3634 
3635  // o Seller is selling for zero XRP.
3636  // o Broker charges no fee.
3637  // o No transfer fee.
3638  //
3639  // Since minter is selling for zero the currency must be XRP.
3640  {
3641  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3642 
3643  uint256 const nftID = mintNFT();
3644 
3645  // minter creates their offer.
3646  uint256 const minterOfferIndex =
3647  keylet::nftoffer(minter, env.seq(minter)).key;
3648  env(token::createOffer(minter, nftID, XRP(0)),
3649  txflags(tfSellNFToken));
3650  env.close();
3651 
3652  // buyer creates their offer. Note: a buy offer can never
3653  // offer zero.
3654  uint256 const buyOfferIndex =
3655  keylet::nftoffer(buyer, env.seq(buyer)).key;
3656  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3657  env.close();
3658 
3659  auto const minterBalance = env.balance(minter);
3660  auto const buyerBalance = env.balance(buyer);
3661  auto const brokerBalance = env.balance(broker);
3662  auto const issuerBalance = env.balance(issuer);
3663 
3664  // Broker charges no brokerFee.
3665  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3666  env.close();
3667 
3668  // Note that minter's XRP balance goes up even though they
3669  // requested XRP(0).
3670  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3671  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3672  BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3673  BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3674 
3675  // Burn the NFT so the next test starts with a clean state.
3676  env(token::burn(buyer, nftID));
3677  env.close();
3678  }
3679 
3680  // o Seller is selling for zero XRP.
3681  // o Broker charges a fee.
3682  // o No transfer fee.
3683  //
3684  // Since minter is selling for zero the currency must be XRP.
3685  {
3686  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3687 
3688  uint256 const nftID = mintNFT();
3689 
3690  // minter creates their offer.
3691  uint256 const minterOfferIndex =
3692  keylet::nftoffer(minter, env.seq(minter)).key;
3693  env(token::createOffer(minter, nftID, XRP(0)),
3694  txflags(tfSellNFToken));
3695  env.close();
3696 
3697  // buyer creates their offer. Note: a buy offer can never
3698  // offer zero.
3699  uint256 const buyOfferIndex =
3700  keylet::nftoffer(buyer, env.seq(buyer)).key;
3701  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3702  env.close();
3703 
3704  // Broker attempts to charge a 1.1 XRP brokerFee and fails.
3705  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3706  token::brokerFee(XRP(1.1)),
3708  env.close();
3709 
3710  auto const minterBalance = env.balance(minter);
3711  auto const buyerBalance = env.balance(buyer);
3712  auto const brokerBalance = env.balance(broker);
3713  auto const issuerBalance = env.balance(issuer);
3714 
3715  // Broker charges a 0.5 XRP brokerFee.
3716  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3717  token::brokerFee(XRP(0.5)));
3718  env.close();
3719 
3720  // Note that minter's XRP balance goes up even though they
3721  // requested XRP(0).
3722  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3723  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3724  BEAST_EXPECT(
3725  env.balance(broker) == brokerBalance + XRP(0.5) - drops(10));
3726  BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3727 
3728  // Burn the NFT so the next test starts with a clean state.
3729  env(token::burn(buyer, nftID));
3730  env.close();
3731  }
3732 
3733  // o Seller is selling for zero XRP.
3734  // o Broker charges no fee.
3735  // o 50% transfer fee.
3736  //
3737  // Since minter is selling for zero the currency must be XRP.
3738  {
3739  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3740 
3741  uint256 const nftID = mintNFT(maxTransferFee);
3742 
3743  // minter creates their offer.
3744  uint256 const minterOfferIndex =
3745  keylet::nftoffer(minter, env.seq(minter)).key;
3746  env(token::createOffer(minter, nftID, XRP(0)),
3747  txflags(tfSellNFToken));
3748  env.close();
3749 
3750  // buyer creates their offer. Note: a buy offer can never
3751  // offer zero.
3752  uint256 const buyOfferIndex =
3753  keylet::nftoffer(buyer, env.seq(buyer)).key;
3754  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3755  env.close();
3756 
3757  auto const minterBalance = env.balance(minter);
3758  auto const buyerBalance = env.balance(buyer);
3759  auto const brokerBalance = env.balance(broker);
3760  auto const issuerBalance = env.balance(issuer);
3761 
3762  // Broker charges no brokerFee.
3763  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3764  env.close();
3765 
3766  // Note that minter's XRP balance goes up even though they
3767  // requested XRP(0).
3768  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3769  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3770  BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3771  BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
3772 
3773  // Burn the NFT so the next test starts with a clean state.
3774  env(token::burn(buyer, nftID));
3775  env.close();
3776  }
3777 
3778  // o Seller is selling for zero XRP.
3779  // o Broker charges 0.5 XRP.
3780  // o 50% transfer fee.
3781  //
3782  // Since minter is selling for zero the currency must be XRP.
3783  {
3784  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3785 
3786  uint256 const nftID = mintNFT(maxTransferFee);
3787 
3788  // minter creates their offer.
3789  uint256 const minterOfferIndex =
3790  keylet::nftoffer(minter, env.seq(minter)).key;
3791  env(token::createOffer(minter, nftID, XRP(0)),
3792  txflags(tfSellNFToken));
3793  env.close();
3794 
3795  // buyer creates their offer. Note: a buy offer can never
3796  // offer zero.
3797  uint256 const buyOfferIndex =
3798  keylet::nftoffer(buyer, env.seq(buyer)).key;
3799  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3800  env.close();
3801 
3802  auto const minterBalance = env.balance(minter);
3803  auto const buyerBalance = env.balance(buyer);
3804  auto const brokerBalance = env.balance(broker);
3805  auto const issuerBalance = env.balance(issuer);
3806 
3807  // Broker charges a 0.75 XRP brokerFee.
3808  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3809  token::brokerFee(XRP(0.75)));
3810  env.close();
3811 
3812  // Note that, with a 50% transfer fee, issuer gets 1/2 of what's
3813  // left _after_ broker takes their fee. minter gets the remainder
3814  // after both broker and minter take their cuts
3815  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
3816  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3817  BEAST_EXPECT(
3818  env.balance(broker) == brokerBalance + XRP(0.75) - drops(10));
3819  BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
3820 
3821  // Burn the NFT so the next test starts with a clean state.
3822  env(token::burn(buyer, nftID));
3823  env.close();
3824  }
3825 
3826  // Lambda to set the balance of all passed in accounts to gwXAU(1000).
3827  auto setXAUBalance_1000 =
3828  [this, &gw, &gwXAU, &env](
3830  accounts,
3831  int line) {
3832  for (Account const& acct : accounts)
3833  {
3834  static const auto xau1000 = gwXAU(1000);
3835  auto const balance = env.balance(acct, gwXAU);
3836  if (balance < xau1000)
3837  {
3838  env(pay(gw, acct, xau1000 - balance));
3839  env.close();
3840  }
3841  else if (balance > xau1000)
3842  {
3843  env(pay(acct, gw, balance - xau1000));
3844  env.close();
3845  }
3846  if (env.balance(acct, gwXAU) != xau1000)
3847  {
3848  std::stringstream ss;
3849  ss << "Unable to set " << acct.human()
3850  << " account balance to gwXAU(1000)";
3851  this->fail(ss.str(), __FILE__, line);
3852  }
3853  }
3854  };
3855 
3856  // The buyer and seller have identical amounts and there is no
3857  // transfer fee.
3858  {
3859  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3860  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
3861 
3862  uint256 const nftID = mintNFT();
3863 
3864  // minter creates their offer.
3865  uint256 const minterOfferIndex =
3866  keylet::nftoffer(minter, env.seq(minter)).key;
3867  env(token::createOffer(minter, nftID, gwXAU(1000)),
3868  txflags(tfSellNFToken));
3869  env.close();
3870 
3871  {
3872  // buyer creates an offer for more XAU than they currently own.
3873  uint256 const buyOfferIndex =
3874  keylet::nftoffer(buyer, env.seq(buyer)).key;
3875  env(token::createOffer(buyer, nftID, gwXAU(1001)),
3876  token::owner(minter));
3877  env.close();
3878 
3879  // broker attempts to broker the offers but cannot.
3880  env(token::brokerOffers(
3881  broker, buyOfferIndex, minterOfferIndex),
3882  ter(tecINSUFFICIENT_FUNDS));
3883  env.close();
3884 
3885  // Cancel buyer's bad offer so the next test starts in a
3886  // clean state.
3887  env(token::cancelOffer(buyer, {buyOfferIndex}));
3888  env.close();
3889  }
3890  {
3891  // buyer creates an offer for less that what minter is asking.
3892  uint256 const buyOfferIndex =
3893  keylet::nftoffer(buyer, env.seq(buyer)).key;
3894  env(token::createOffer(buyer, nftID, gwXAU(999)),
3895  token::owner(minter));
3896  env.close();
3897 
3898  // broker attempts to broker the offers but cannot.
3899  env(token::brokerOffers(
3900  broker, buyOfferIndex, minterOfferIndex),
3902  env.close();
3903 
3904  // Cancel buyer's bad offer so the next test starts in a
3905  // clean state.
3906  env(token::cancelOffer(buyer, {buyOfferIndex}));
3907  env.close();
3908  }
3909 
3910  // buyer creates a large enough offer.
3911  uint256 const buyOfferIndex =
3912  keylet::nftoffer(buyer, env.seq(buyer)).key;
3913  env(token::createOffer(buyer, nftID, gwXAU(1000)),
3914  token::owner(minter));
3915  env.close();
3916 
3917  // Broker attempts to charge a brokerFee but cannot.
3918  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3919  token::brokerFee(gwXAU(0.1)),
3921  env.close();
3922 
3923  // broker charges no brokerFee and succeeds.
3924  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3925  env.close();
3926 
3927  BEAST_EXPECT(ownerCount(env, issuer) == 1);
3928  BEAST_EXPECT(ownerCount(env, minter) == 1);
3929  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3930  BEAST_EXPECT(ownerCount(env, broker) == 1);
3931  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
3932  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
3933  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
3934  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
3935 
3936  // Burn the NFT so the next test starts with a clean state.
3937  env(token::burn(buyer, nftID));
3938  env.close();
3939  }
3940 
3941  // seller offers more than buyer is asking.
3942  // There are both transfer and broker fees.
3943  {
3944  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3945  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
3946 
3947  uint256 const nftID = mintNFT(maxTransferFee);
3948 
3949  // minter creates their offer.
3950  uint256 const minterOfferIndex =
3951  keylet::nftoffer(minter, env.seq(minter)).key;
3952  env(token::createOffer(minter, nftID, gwXAU(900)),
3953  txflags(tfSellNFToken));
3954  env.close();
3955  {
3956  // buyer creates an offer for more XAU than they currently own.
3957  uint256 const buyOfferIndex =
3958  keylet::nftoffer(buyer, env.seq(buyer)).key;
3959  env(token::createOffer(buyer, nftID, gwXAU(1001)),
3960  token::owner(minter));
3961  env.close();
3962 
3963  // broker attempts to broker the offers but cannot.
3964  env(token::brokerOffers(
3965  broker, buyOfferIndex, minterOfferIndex),
3966  ter(tecINSUFFICIENT_FUNDS));
3967  env.close();
3968 
3969  // Cancel buyer's bad offer so the next test starts in a
3970  // clean state.
3971  env(token::cancelOffer(buyer, {buyOfferIndex}));
3972  env.close();
3973  }
3974  {
3975  // buyer creates an offer for less that what minter is asking.
3976  uint256 const buyOfferIndex =
3977  keylet::nftoffer(buyer, env.seq(buyer)).key;
3978  env(token::createOffer(buyer, nftID, gwXAU(899)),
3979  token::owner(minter));
3980  env.close();
3981 
3982  // broker attempts to broker the offers but cannot.
3983  env(token::brokerOffers(
3984  broker, buyOfferIndex, minterOfferIndex),
3986  env.close();
3987 
3988  // Cancel buyer's bad offer so the next test starts in a
3989  // clean state.
3990  env(token::cancelOffer(buyer, {buyOfferIndex}));
3991  env.close();
3992  }
3993  // buyer creates a large enough offer.
3994  uint256 const buyOfferIndex =
3995  keylet::nftoffer(buyer, env.seq(buyer)).key;
3996  env(token::createOffer(buyer, nftID, gwXAU(1000)),
3997  token::owner(minter));
3998  env.close();
3999 
4000  // Broker attempts to charge a brokerFee larger than the
4001  // difference between the two offers but cannot.
4002  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4003  token::brokerFee(gwXAU(101)),
4005  env.close();
4006 
4007  // broker charges the full difference between the two offers and
4008  // succeeds.
4009  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4010  token::brokerFee(gwXAU(100)));
4011  env.close();
4012 
4013  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4014  BEAST_EXPECT(ownerCount(env, minter) == 1);
4015  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4016  BEAST_EXPECT(ownerCount(env, broker) == 1);
4017  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4018  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4019  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4020  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4021 
4022  // Burn the NFT so the next test starts with a clean state.
4023  env(token::burn(buyer, nftID));
4024  env.close();
4025  }
4026  // seller offers more than buyer is asking.
4027  // There are both transfer and broker fees, but broker takes less than
4028  // the maximum.
4029  {
4030  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4031  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4032 
4033  uint256 const nftID = mintNFT(maxTransferFee / 2); // 25%
4034 
4035  // minter creates their offer.
4036  uint256 const minterOfferIndex =
4037  keylet::nftoffer(minter, env.seq(minter)).key;
4038  env(token::createOffer(minter, nftID, gwXAU(900)),
4039  txflags(tfSellNFToken));
4040  env.close();
4041 
4042  // buyer creates a large enough offer.
4043  uint256 const buyOfferIndex =
4044  keylet::nftoffer(buyer, env.seq(buyer)).key;
4045  env(token::createOffer(buyer, nftID, gwXAU(1000)),
4046  token::owner(minter));
4047  env.close();
4048 
4049  // broker charges half difference between the two offers and
4050  // succeeds. 25% of the remaining difference goes to issuer.
4051  // The rest goes to minter.
4052  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4053  token::brokerFee(gwXAU(50)));
4054  env.close();
4055 
4056  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4057  BEAST_EXPECT(ownerCount(env, minter) == 1);
4058  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4059  BEAST_EXPECT(ownerCount(env, broker) == 1);
4060  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4061  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4062  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4063  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4064 
4065  // Burn the NFT so the next test starts with a clean state.
4066  env(token::burn(buyer, nftID));
4067  env.close();
4068  }
4069  }
4070 
4071  void
4073  {
4074  // Verify the Owner field of an offer behaves as expected.
4075  testcase("NFToken offer owner");
4076 
4077  using namespace test::jtx;
4078 
4079  Env env{*this, features};
4080 
4081  Account const issuer{"issuer"};
4082  Account const buyer1{"buyer1"};
4083  Account const buyer2{"buyer2"};
4084  env.fund(XRP(10000), issuer, buyer1, buyer2);
4085  env.close();
4086 
4087  // issuer creates an NFT.
4088  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4089  env(token::mint(issuer, 0u), txflags(tfTransferable));
4090  env.close();
4091 
4092  // Prove that issuer now owns nftId.
4093  BEAST_EXPECT(nftCount(env, issuer) == 1);
4094  BEAST_EXPECT(nftCount(env, buyer1) == 0);
4095  BEAST_EXPECT(nftCount(env, buyer2) == 0);
4096 
4097  // Both buyer1 and buyer2 create buy offers for nftId.
4098  uint256 const buyer1OfferIndex =
4099  keylet::nftoffer(buyer1, env.seq(buyer1)).key;
4100  env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4101  uint256 const buyer2OfferIndex =
4102  keylet::nftoffer(buyer2, env.seq(buyer2)).key;
4103  env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4104  env.close();
4105 
4106  // Lambda that counts the number of buy offers for a given NFT.
4107  auto nftBuyOfferCount = [&env](uint256 const& nftId) -> std::size_t {
4108  // We know that in this case not very many offers will be
4109  // returned, so we skip the marker stuff.
4110  Json::Value params;
4111  params[jss::nft_id] = to_string(nftId);
4112  Json::Value buyOffers =
4113  env.rpc("json", "nft_buy_offers", to_string(params));
4114 
4115  if (buyOffers.isMember(jss::result) &&
4116  buyOffers[jss::result].isMember(jss::offers))
4117  return buyOffers[jss::result][jss::offers].size();
4118 
4119  return 0;
4120  };
4121 
4122  // Show there are two buy offers for nftId.
4123  BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4124 
4125  // issuer accepts buyer1's offer.
4126  env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4127  env.close();
4128 
4129  // Prove that buyer1 now owns nftId.
4130  BEAST_EXPECT(nftCount(env, issuer) == 0);
4131  BEAST_EXPECT(nftCount(env, buyer1) == 1);
4132  BEAST_EXPECT(nftCount(env, buyer2) == 0);
4133 
4134  // buyer1's offer was consumed, but buyer2's offer is still in the
4135  // ledger.
4136  BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4137 
4138  // buyer1 can now accept buyer2's offer, even though buyer2's
4139  // NFTokenCreateOffer transaction specified the NFT Owner as issuer.
4140  env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4141  env.close();
4142 
4143  // Prove that buyer2 now owns nftId.
4144  BEAST_EXPECT(nftCount(env, issuer) == 0);
4145  BEAST_EXPECT(nftCount(env, buyer1) == 0);
4146  BEAST_EXPECT(nftCount(env, buyer2) == 1);
4147 
4148  // All of the NFTokenOffers are now consumed.
4149  BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4150  }
4151 
4152  void
4154  {
4155  // Make sure all NFToken transactions work with tickets.
4156  testcase("NFToken transactions with tickets");
4157 
4158  using namespace test::jtx;
4159 
4160  Env env{*this, features};
4161 
4162  Account const issuer{"issuer"};
4163  Account const buyer{"buyer"};
4164  env.fund(XRP(10000), issuer, buyer);
4165  env.close();
4166 
4167  // issuer and buyer grab enough tickets for all of the following
4168  // transactions. Note that once the tickets are acquired issuer's
4169  // and buyer's account sequence numbers should not advance.
4170  std::uint32_t issuerTicketSeq{env.seq(issuer) + 1};
4171  env(ticket::create(issuer, 10));
4172  env.close();
4173  std::uint32_t const issuerSeq{env.seq(issuer)};
4174  BEAST_EXPECT(ticketCount(env, issuer) == 10);
4175 
4176  std::uint32_t buyerTicketSeq{env.seq(buyer) + 1};
4177  env(ticket::create(buyer, 10));
4178  env.close();
4179  std::uint32_t const buyerSeq{env.seq(buyer)};
4180  BEAST_EXPECT(ticketCount(env, buyer) == 10);
4181 
4182  // NFTokenMint
4183  BEAST_EXPECT(ownerCount(env, issuer) == 10);
4184  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4185  env(token::mint(issuer, 0u),
4186  txflags(tfTransferable),
4187  ticket::use(issuerTicketSeq++));
4188  env.close();
4189  BEAST_EXPECT(ownerCount(env, issuer) == 10);
4190  BEAST_EXPECT(ticketCount(env, issuer) == 9);
4191 
4192  // NFTokenCreateOffer
4193  BEAST_EXPECT(ownerCount(env, buyer) == 10);
4194  uint256 const offerIndex0 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4195  env(token::createOffer(buyer, nftId, XRP(1)),
4196  token::owner(issuer),
4197  ticket::use(buyerTicketSeq++));
4198  env.close();
4199  BEAST_EXPECT(ownerCount(env, buyer) == 10);
4200  BEAST_EXPECT(ticketCount(env, buyer) == 9);
4201 
4202  // NFTokenCancelOffer
4203  env(token::cancelOffer(buyer, {offerIndex0}),
4204  ticket::use(buyerTicketSeq++));
4205  env.close();
4206  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4207  BEAST_EXPECT(ticketCount(env, buyer) == 8);
4208 
4209  // NFTokenCreateOffer. buyer tries again.
4210  uint256 const offerIndex1 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4211  env(token::createOffer(buyer, nftId, XRP(2)),
4212  token::owner(issuer),
4213  ticket::use(buyerTicketSeq++));
4214  env.close();
4215  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4216  BEAST_EXPECT(ticketCount(env, buyer) == 7);
4217 
4218  // NFTokenAcceptOffer. issuer accepts buyer's offer.
4219  env(token::acceptBuyOffer(issuer, offerIndex1),
4220  ticket::use(issuerTicketSeq++));
4221  env.close();
4222  BEAST_EXPECT(ownerCount(env, issuer) == 8);
4223  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4224  BEAST_EXPECT(ticketCount(env, issuer) == 8);
4225 
4226  // NFTokenBurn. buyer burns the token they just bought.
4227  env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4228  env.close();
4229  BEAST_EXPECT(ownerCount(env, issuer) == 8);
4230  BEAST_EXPECT(ownerCount(env, buyer) == 6);
4231  BEAST_EXPECT(ticketCount(env, buyer) == 6);
4232 
4233  // Verify that the account sequence numbers did not advance.
4234  BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4235  BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4236  }
4237 
4238  void
4240  {
4241  // Account deletion rules with NFTs:
4242  // 1. An account holding one or more NFT offers may be deleted.
4243  // 2. An NFT issuer with any NFTs they have issued still in the
4244  // ledger may not be deleted.
4245  // 3. An account holding one or more NFTs may not be deleted.
4246  testcase("NFToken delete account");
4247 
4248  using namespace test::jtx;
4249 
4250  Env env{*this, features};
4251 
4252  Account const issuer{"issuer"};
4253  Account const minter{"minter"};
4254  Account const becky{"becky"};
4255  Account const carla{"carla"};
4256  Account const daria{"daria"};
4257 
4258  env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4259  env.close();
4260 
4261  // Allow enough ledgers to pass so any of these accounts can be deleted.
4262  for (int i = 0; i < 300; ++i)
4263  env.close();
4264 
4265  env(token::setMinter(issuer, minter));
4266  env.close();
4267 
4268  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4269  env(token::mint(minter, 0u),
4270  token::issuer(issuer),
4271  txflags(tfTransferable));
4272  env.close();
4273 
4274  // At the momement issuer and minter cannot delete themselves.
4275  // o issuer has an issued NFT in the ledger.
4276  // o minter owns an NFT.
4277  env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4278  env(acctdelete(minter, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4279  env.close();
4280 
4281  // Let enough ledgers pass so the account delete transactions are
4282  // not retried.
4283  for (int i = 0; i < 15; ++i)
4284  env.close();
4285 
4286  // becky and carla create offers for minter's NFT.
4287  env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4288  env.close();
4289 
4290  uint256 const carlaOfferIndex =
4291  keylet::nftoffer(carla, env.seq(carla)).key;
4292  env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4293  env.close();
4294 
4295  // It should be possible for becky to delete herself, even though
4296  // becky has an active NFT offer.
4297  env(acctdelete(becky, daria), fee(XRP(50)));
4298  env.close();
4299 
4300  // minter accepts carla's offer.
4301  env(token::acceptBuyOffer(minter, carlaOfferIndex));
4302  env.close();
4303 
4304  // Now it should be possible for minter to delete themselves since
4305  // they no longer own an NFT.
4306  env(acctdelete(minter, daria), fee(XRP(50)));
4307  env.close();
4308 
4309  // 1. issuer cannot delete themselves because they issued an NFT that
4310  // is still in the ledger.
4311  // 2. carla owns an NFT, so she cannot delete herself.
4312  env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4313  env(acctdelete(carla, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4314  env.close();
4315 
4316  // Let enough ledgers pass so the account delete transactions are
4317  // not retried.
4318  for (int i = 0; i < 15; ++i)
4319  env.close();
4320 
4321  // carla burns her NFT. Since issuer's NFT is no longer in the
4322  // ledger, both issuer and carla can delete themselves.
4323  env(token::burn(carla, nftId));
4324  env.close();
4325 
4326  env(acctdelete(issuer, daria), fee(XRP(50)));
4327  env(acctdelete(carla, daria), fee(XRP(50)));
4328  env.close();
4329  }
4330 
4331  void
4333  {
4334  testcase("nft_buy_offers and nft_sell_offers");
4335 
4336  // The default limit on returned NFToken offers is 250, so we need
4337  // to produce more than 250 offers of each kind in order to exercise
4338  // the marker.
4339 
4340  // Fortunately there's nothing in the rules that says an account
4341  // can't hold more than one offer for the same NFT. So we only
4342  // need two accounts to generate the necessary offers.
4343  using namespace test::jtx;
4344 
4345  Env env{*this, features};
4346 
4347  Account const issuer{"issuer"};
4348  Account const buyer{"buyer"};
4349 
4350  // A lot of offers requires a lot for reserve.
4351  env.fund(XRP(1000000), issuer, buyer);
4352  env.close();
4353 
4354  // Create an NFT that we'll make offers for.
4355  uint256 const nftID{token::getNextID(env, issuer, 0u, tfTransferable)};
4356  env(token::mint(issuer, 0), txflags(tfTransferable));
4357  env.close();
4358 
4359  // A lambda that validates nft_XXX_offers query responses.
4360  auto checkOffers = [this, &env, &nftID](
4361  char const* request,
4362  int expectCount,
4363  int expectMarkerCount,
4364  int line) {
4365  int markerCount = 0;
4366  Json::Value allOffers(Json::arrayValue);
4367  std::string marker;
4368 
4369  // The do/while collects results until no marker is returned.
4370  do
4371  {
4372  Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4373  Json::Value params;
4374  params[jss::nft_id] = to_string(nftID);
4375 
4376  if (!marker.empty())
4377  params[jss::marker] = marker;
4378  return env.rpc("json", request, to_string(params));
4379  }();
4380 
4381  // If there are no offers for the NFT we get an error
4382  if (expectCount == 0)
4383  {
4384  if (expect(
4385  nftOffers.isMember(jss::result),
4386  "expected \"result\"",
4387  __FILE__,
4388  line))
4389  {
4390  if (expect(
4391  nftOffers[jss::result].isMember(jss::error),
4392  "expected \"error\"",
4393  __FILE__,
4394  line))
4395  {
4396  expect(
4397  nftOffers[jss::result][jss::error].asString() ==
4398  "objectNotFound",
4399  "expected \"objectNotFound\"",
4400  __FILE__,
4401  line);
4402  }
4403  }
4404  break;
4405  }
4406 
4407  marker.clear();
4408  if (expect(
4409  nftOffers.isMember(jss::result),
4410  "expected \"result\"",
4411  __FILE__,
4412  line))
4413  {
4414  Json::Value& result = nftOffers[jss::result];
4415 
4416  if (result.isMember(jss::marker))
4417  {
4418  ++markerCount;
4419  marker = result[jss::marker].asString();
4420  }
4421 
4422  if (expect(
4423  result.isMember(jss::offers),
4424  "expected \"offers\"",
4425  __FILE__,
4426  line))
4427  {
4428  Json::Value& someOffers = result[jss::offers];
4429  for (std::size_t i = 0; i < someOffers.size(); ++i)
4430  allOffers.append(someOffers[i]);
4431  }
4432  }
4433  } while (!marker.empty());
4434 
4435  // Verify the contents of allOffers makes sense.
4436  expect(
4437  allOffers.size() == expectCount,
4438  "Unexpected returned offer count",
4439  __FILE__,
4440  line);
4441  expect(
4442  markerCount == expectMarkerCount,
4443  "Unexpected marker count",
4444  __FILE__,
4445  line);
4446  std::optional<int> globalFlags;
4447  std::set<std::string> offerIndexes;
4448  std::set<std::string> amounts;
4449  for (Json::Value const& offer : allOffers)
4450  {
4451  // The flags on all found offers should be the same.
4452  if (!globalFlags)
4453  globalFlags = offer[jss::flags].asInt();
4454 
4455  expect(
4456  *globalFlags == offer[jss::flags].asInt(),
4457  "Inconsistent flags returned",
4458  __FILE__,
4459  line);
4460 
4461  // The test conditions should produce unique indexes and
4462  // amounts for all offers.
4463  offerIndexes.insert(offer[jss::nft_offer_index].asString());
4464  amounts.insert(offer[jss::amount].asString());
4465  }
4466 
4467  expect(
4468  offerIndexes.size() == expectCount,
4469  "Duplicate indexes returned?",
4470  __FILE__,
4471  line);
4472  expect(
4473  amounts.size() == expectCount,
4474  "Duplicate amounts returned?",
4475  __FILE__,
4476  line);
4477  };
4478 
4479  // There are no sell offers.
4480  checkOffers("nft_sell_offers", 0, false, __LINE__);
4481 
4482  // A lambda that generates sell offers.
4483  STAmount sellPrice = XRP(0);
4484  auto makeSellOffers =
4485  [&env, &issuer, &nftID, &sellPrice](STAmount const& limit) {
4486  // Save a little test time by not closing too often.
4487  int offerCount = 0;
4488  while (sellPrice < limit)
4489  {
4490  sellPrice += XRP(1);
4491  env(token::createOffer(issuer, nftID, sellPrice),
4492  txflags(tfSellNFToken));
4493  if (++offerCount % 10 == 0)
4494  env.close();
4495  }
4496  env.close();
4497  };
4498 
4499  // There is one sell offer.
4500  makeSellOffers(XRP(1));
4501  checkOffers("nft_sell_offers", 1, 0, __LINE__);
4502 
4503  // There are 250 sell offers.
4504  makeSellOffers(XRP(250));
4505  checkOffers("nft_sell_offers", 250, 0, __LINE__);
4506 
4507  // There are 251 sell offers.
4508  makeSellOffers(XRP(251));
4509  checkOffers("nft_sell_offers", 251, 1, __LINE__);
4510 
4511  // There are 500 sell offers.
4512  makeSellOffers(XRP(500));
4513  checkOffers("nft_sell_offers", 500, 1, __LINE__);
4514 
4515  // There are 501 sell offers.
4516  makeSellOffers(XRP(501));
4517  checkOffers("nft_sell_offers", 501, 2, __LINE__);
4518 
4519  // There are no buy offers.
4520  checkOffers("nft_buy_offers", 0, 0, __LINE__);
4521 
4522  // A lambda that generates buy offers.
4523  STAmount buyPrice = XRP(0);
4524  auto makeBuyOffers =
4525  [&env, &buyer, &issuer, &nftID, &buyPrice](STAmount const& limit) {
4526  // Save a little test time by not closing too often.
4527  int offerCount = 0;
4528  while (buyPrice < limit)
4529  {
4530  buyPrice += XRP(1);
4531  env(token::createOffer(buyer, nftID, buyPrice),
4532  token::owner(issuer));
4533  if (++offerCount % 10 == 0)
4534  env.close();
4535  }
4536  env.close();
4537  };
4538 
4539  // There is one buy offer;
4540  makeBuyOffers(XRP(1));
4541  checkOffers("nft_buy_offers", 1, 0, __LINE__);
4542 
4543  // There are 250 buy offers.
4544  makeBuyOffers(XRP(250));
4545  checkOffers("nft_buy_offers", 250, 0, __LINE__);
4546 
4547  // There are 251 buy offers.
4548  makeBuyOffers(XRP(251));
4549  checkOffers("nft_buy_offers", 251, 1, __LINE__);
4550 
4551  // There are 500 buy offers.
4552  makeBuyOffers(XRP(500));
4553  checkOffers("nft_buy_offers", 500, 1, __LINE__);
4554 
4555  // There are 501 buy offers.
4556  makeBuyOffers(XRP(501));
4557  checkOffers("nft_buy_offers", 501, 2, __LINE__);
4558  }
4559 
4560  void
4562  {
4563  testEnabled(features);
4564  testMintReserve(features);
4565  testMintMaxTokens(features);
4566  testMintInvalid(features);
4567  testBurnInvalid(features);
4568  testCreateOfferInvalid(features);
4569  testCancelOfferInvalid(features);
4570  testAcceptOfferInvalid(features);
4571  testMintFlagBurnable(features);
4572  testMintFlagOnlyXRP(features);
4573  testMintFlagCreateTrustLine(features);
4574  testMintFlagTransferable(features);
4575  testMintTransferFee(features);
4576  testMintTaxon(features);
4577  testMintURI(features);
4578  testCreateOfferDestination(features);
4579  testCreateOfferExpiration(features);
4580  testCancelOffers(features);
4581  testCancelTooManyOffers(features);
4582  testBrokeredAccept(features);
4583  testNFTokenOfferOwner(features);
4584  testNFTokenWithTickets(features);
4585  testNFTokenDeleteAccount(features);
4586  testNftXxxOffers(features);
4587  }
4588 
4589 public:
4590  void
4591  run() override
4592  {
4593  using namespace test::jtx;
4594  FeatureBitset const all{supported_amendments()};
4595  FeatureBitset const fixNFTDir{fixNFTokenDirV1};
4596 
4597  testWithFeats(all - fixNFTDir);
4598  testWithFeats(all);
4599  }
4600 };
4601 
4602 BEAST_DEFINE_TESTSUITE_PRIO(NFToken, tx, ripple, 2);
4603 
4604 } // namespace ripple
ripple::NFToken_test::testNFTokenDeleteAccount
void testNFTokenDeleteAccount(FeatureBitset features)
Definition: NFToken_test.cpp:4239
ripple::tecUNFUNDED_OFFER
@ tecUNFUNDED_OFFER
Definition: TER.h:248
ripple::maxTransferFee
constexpr std::uint16_t maxTransferFee
The maximum token transfer fee allowed.
Definition: Protocol.h:81
ripple::sfOwnerCount
const SF_UINT32 sfOwnerCount
ripple::NFToken_test::testMintMaxTokens
void testMintMaxTokens(FeatureBitset features)
Definition: NFToken_test.cpp:442
ripple::NFToken_test::testBurnInvalid
void testBurnInvalid(FeatureBitset features)
Definition: NFToken_test.cpp:573
ripple::tecOBJECT_NOT_FOUND
@ tecOBJECT_NOT_FOUND
Definition: TER.h:290
ripple::tecFROZEN
@ tecFROZEN
Definition: TER.h:267
ripple::NFToken_test::testMintFlagBurnable
void testMintFlagBurnable(FeatureBitset features)
Definition: NFToken_test.cpp:1355
ripple::NFToken_test::testCreateOfferInvalid
void testCreateOfferInvalid(FeatureBitset features)
Definition: NFToken_test.cpp:633
ripple::tfTransferable
constexpr const std::uint32_t tfTransferable
Definition: TxFlags.h:121
ripple::NFToken_test::nftCount
static std::uint32_t nftCount(test::jtx::Env &env, test::jtx::Account const &acct)
Definition: NFToken_test.cpp:64
std::string
STL class.
ripple::STAmount::cMinValue
static const std::uint64_t cMinValue
Definition: STAmount.h:65
ripple::NFToken_test::testMintReserve
void testMintReserve(FeatureBitset features)
Definition: NFToken_test.cpp:201
ripple::NFToken_test::testCreateOfferExpiration
void testCreateOfferExpiration(FeatureBitset features)
Definition: NFToken_test.cpp:2863
ripple::NFToken_test::testNFTokenOfferOwner
void testNFTokenOfferOwner(FeatureBitset features)
Definition: NFToken_test.cpp:4072
ripple::sfNFTokenOffers
const SF_VECTOR256 sfNFTokenOffers
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::tecINSUFFICIENT_FUNDS
@ tecINSUFFICIENT_FUNDS
Definition: TER.h:289
std::string::reserve
T reserve(T... args)
ripple::TxSearched::all
@ all
ripple::OpenView
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:55
std::vector
STL class.
std::set::size
T size(T... args)
ripple::STAmount::getJson
Json::Value getJson(JsonOptions) const override
Definition: STAmount.cpp:614
ripple::sfMintedNFTokens
const SF_UINT32 sfMintedNFTokens
ripple::keylet::nftoffer
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition: Indexes.cpp:355
ripple::test::jtx::Account::human
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
std::stringstream
STL class.
ripple::NFToken_test::testCancelTooManyOffers
void testCancelTooManyOffers(FeatureBitset features)
Definition: NFToken_test.cpp:3451
ripple::STAmount::cMinOffset
static const int cMinOffset
Definition: STAmount.h:61
ripple::NFToken_test::ownerCount
static std::uint32_t ownerCount(test::jtx::Env const &env, test::jtx::Account const &acct)
Definition: NFToken_test.cpp:34
ripple::tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
@ tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
Definition: TER.h:288
ripple::nft::toTaxon
Taxon toTaxon(std::uint32_t i)
Definition: NFTokenUtils.h:40
ripple::BEAST_DEFINE_TESTSUITE_PRIO
BEAST_DEFINE_TESTSUITE_PRIO(NFToken, tx, ripple, 2)
ripple::NFToken_test::testMintFlagOnlyXRP
void testMintFlagOnlyXRP(FeatureBitset features)
Definition: NFToken_test.cpp:1473
ripple::NFToken_test::testAcceptOfferInvalid
void testAcceptOfferInvalid(FeatureBitset features)
Definition: NFToken_test.cpp:1003
ripple::SField::jsonName
const Json::StaticString jsonName
Definition: SField.h:136
std::sort
T sort(T... args)
std::string::clear
T clear(T... args)
ripple::NFToken_test::ticketCount
static std::uint32_t ticketCount(test::jtx::Env const &env, test::jtx::Account const &acct)
Definition: NFToken_test.cpp:75
std::string::push_back
T push_back(T... args)
ripple::NFToken_test::testCancelOffers
void testCancelOffers(FeatureBitset features)
Definition: NFToken_test.cpp:3337
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint< 256 >
ripple::NFToken_test::testWithFeats
void testWithFeats(FeatureBitset features)
Definition: NFToken_test.cpp:4561
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:106
ripple::tecNFTOKEN_OFFER_TYPE_MISMATCH
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
Definition: TER.h:287
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::rand_int
std::enable_if_t< std::is_integral< Integral >::value &&detail::is_engine< Engine >::value, Integral > rand_int(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
Definition: ripple/basics/random.h:115
ripple::tfBurnable
constexpr const std::uint32_t tfBurnable
Definition: TxFlags.h:118
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
ripple::tefNFTOKEN_IS_NOT_TRANSFERABLE
@ tefNFTOKEN_IS_NOT_TRANSFERABLE
Definition: TER.h:165
ripple::sfNFTokenMinter
const SF_ACCOUNT sfNFTokenMinter
ripple::NFToken_test::run
void run() override
Definition: NFToken_test.cpp:4591
ripple::JsonOptions::none
@ none
ripple::TERSubset< CanCvtToTER >
ripple::NFToken_test::testNftXxxOffers
void testNftXxxOffers(FeatureBitset features)
Definition: NFToken_test.cpp:4332
ripple::NFToken_test::mintedCount
static std::uint32_t mintedCount(test::jtx::Env const &env, test::jtx::Account const &issuer)
Definition: NFToken_test.cpp:44
ripple::NFToken_test::testMintTaxon
void testMintTaxon(FeatureBitset features)
Definition: NFToken_test.cpp:2457
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:565
std::to_string
T to_string(T... args)
ripple::rand_byte
std::enable_if_t<(std::is_same< Byte, unsigned char >::value||std::is_same< Byte, std::uint8_t >::value) &&detail::is_engine< Engine >::value, Byte > rand_byte(Engine &engine)
Return a random byte.
Definition: ripple/basics/random.h:173
ripple::set
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:313
ripple::sfTicketCount
const SF_UINT32 sfTicketCount
ripple::NFToken_test::testMintInvalid
void testMintInvalid(FeatureBitset features)
Definition: NFToken_test.cpp:497
ripple::STAmount
Definition: STAmount.h:44
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
ripple::tfSellNFToken
constexpr const std::uint32_t tfSellNFToken
Definition: TxFlags.h:127
ripple::temBAD_AMOUNT
@ temBAD_AMOUNT
Definition: TER.h:84
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::maxTokenOfferCancelCount
constexpr std::size_t maxTokenOfferCancelCount
The maximum number of token offers that can be canceled at once.
Definition: Protocol.h:67
ripple::NFToken_test
Definition: NFToken_test.cpp:30
std::uint32_t
ripple::NFToken_test::burnedCount
static std::uint32_t burnedCount(test::jtx::Env const &env, test::jtx::Account const &issuer)
Definition: NFToken_test.cpp:54
ripple::NFToken_test::testMintTransferFee
void testMintTransferFee(FeatureBitset features)
Definition: NFToken_test.cpp:2026
ripple::fixNFTokenDirV1
const uint256 fixNFTokenDirV1
ripple::temBAD_FEE
@ temBAD_FEE
Definition: TER.h:87
ripple::maxTokenURILength
constexpr std::size_t maxTokenURILength
The maximum length of a URI inside an NFT.
Definition: Protocol.h:84
ripple::NFToken_test::testMintFlagTransferable
void testMintFlagTransferable(FeatureBitset features)
Definition: NFToken_test.cpp:1736
ripple::sfURI
const SF_VL sfURI
ripple::NFToken_test::testCancelOfferInvalid
void testCancelOfferInvalid(FeatureBitset features)
Definition: NFToken_test.cpp:883
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::featureNonFungibleTokensV1
const uint256 featureNonFungibleTokensV1
Json::Value::removeMember
Value removeMember(const char *key)
Remove and return the named member.
Definition: json_value.cpp:907
ripple::tecNFTOKEN_BUY_SELL_MISMATCH
@ tecNFTOKEN_BUY_SELL_MISMATCH
Definition: TER.h:286
ripple::tecINSUFFICIENT_PAYMENT
@ tecINSUFFICIENT_PAYMENT
Definition: TER.h:291
ripple::NFToken_test::testBrokeredAccept
void testBrokeredAccept(FeatureBitset features)
Definition: NFToken_test.cpp:3570
ripple::tfSetFreeze
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:102
ripple::tecNO_LINE
@ tecNO_LINE
Definition: TER.h:265
ripple::tecEXPIRED
@ tecEXPIRED
Definition: TER.h:278
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:109
ripple::NFToken_test::lastClose
std::uint32_t lastClose(test::jtx::Env &env)
Definition: NFToken_test.cpp:85
std::vector::begin
T begin(T... args)
std::set::insert
T insert(T... args)
ripple::test::jtx::Env::le
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:213
ripple::tecNO_ISSUER
@ tecNO_ISSUER
Definition: TER.h:263
ripple::tecHAS_OBLIGATIONS
@ tecHAS_OBLIGATIONS
Definition: TER.h:281
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:269
ripple::FeatureBitset
Definition: Feature.h:113
ripple::NFToken_test::testEnabled
void testEnabled(FeatureBitset features)
Definition: NFToken_test.cpp:91
ripple::NFToken_test::testMintFlagCreateTrustLine
void testMintFlagCreateTrustLine(FeatureBitset features)
Definition: NFToken_test.cpp:1567
ripple::tecINSUFFICIENT_RESERVE
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:271
std::string::empty
T empty(T... args)
ripple::NFToken_test::testNFTokenWithTickets
void testNFTokenWithTickets(FeatureBitset features)
Definition: NFToken_test.cpp:4153
ripple::OpenView::read
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition: OpenView.cpp:171
ripple::tfTrustLine
constexpr const std::uint32_t tfTrustLine
Definition: TxFlags.h:120
std::optional< int >
std::stringstream::str
T str(T... args)
std::size_t
ripple::tfClearFreeze
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:103
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::tfOnlyXRP
constexpr const std::uint32_t tfOnlyXRP
Definition: TxFlags.h:119
ripple::sfNFTokenTaxon
const SF_UINT32 sfNFTokenTaxon
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:45
ripple::tecNO_ENTRY
@ tecNO_ENTRY
Definition: TER.h:270
std::vector::end
T end(T... args)
ripple::temMALFORMED
@ temMALFORMED
Definition: TER.h:82
ripple::tagged_integer
A type-safe wrap around standard integral types.
Definition: tagged_integer.h:44
ripple::temBAD_EXPIRATION
@ temBAD_EXPIRATION
Definition: TER.h:86
ripple::sfBurnedNFTokens
const SF_UINT32 sfBurnedNFTokens
ripple::keylet::check
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition: Indexes.cpp:281
ripple::nft::getTaxon
Taxon getTaxon(uint256 const &id)
Definition: NFTokenUtils.h:164
ripple::NFToken_test::testMintURI
void testMintURI(FeatureBitset features)
Definition: NFToken_test.cpp:2530
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:219
ripple::NFToken_test::testCreateOfferDestination
void testCreateOfferDestination(FeatureBitset features)
Definition: NFToken_test.cpp:2640
ripple::temBAD_NFTOKEN_TRANSFER_FEE
@ temBAD_NFTOKEN_TRANSFER_FEE
Definition: TER.h:122
std::set< std::string >
ripple::sfNFTokenSellOffer
const SF_UINT256 sfNFTokenSellOffer
ripple::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:300
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::tecNO_DST
@ tecNO_DST
Definition: TER.h:254
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:684
ripple::sfNFTokenBrokerFee
const SF_AMOUNT sfNFTokenBrokerFee
Json::Value
Represents a JSON value.
Definition: json_value.h:145
initializer_list
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469