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