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