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