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  {
2353  // An nft with a transfer fee of 1 basis point.
2354  uint256 const nftID =
2355  token::getNextID(env, alice, 0u, tfTransferable, 1);
2356  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2357  env.close();
2358 
2359  // minter buys the nft for XRP(1). Since the transfer involves
2360  // alice there should be no transfer fee.
2361  STAmount fee = drops(10);
2362  STAmount aliceBalance = env.balance(alice);
2363  STAmount minterBalance = env.balance(minter);
2364  uint256 const minterBuyOfferIndex =
2365  keylet::nftoffer(minter, env.seq(minter)).key;
2366  env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2367  env.close();
2368  env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2369  env.close();
2370  aliceBalance += XRP(1) - fee;
2371  minterBalance -= XRP(1) + fee;
2372  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2373  BEAST_EXPECT(env.balance(minter) == minterBalance);
2374 
2375  // minter sells to carol. The payment is just small enough that
2376  // alice does not get any transfer fee.
2377  STAmount carolBalance = env.balance(carol);
2378  uint256 const minterSellOfferIndex =
2379  keylet::nftoffer(minter, env.seq(minter)).key;
2380  env(token::createOffer(minter, nftID, drops(99999)),
2381  txflags(tfSellNFToken));
2382  env.close();
2383  env(token::acceptSellOffer(carol, minterSellOfferIndex));
2384  env.close();
2385  minterBalance += drops(99999) - fee;
2386  carolBalance -= drops(99999) + fee;
2387  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2388  BEAST_EXPECT(env.balance(minter) == minterBalance);
2389  BEAST_EXPECT(env.balance(carol) == carolBalance);
2390 
2391  // carol sells to becky. This is the smallest amount to pay for a
2392  // transfer that enables a transfer fee of 1 basis point.
2393  STAmount beckyBalance = env.balance(becky);
2394  uint256 const beckyBuyOfferIndex =
2395  keylet::nftoffer(becky, env.seq(becky)).key;
2396  env(token::createOffer(becky, nftID, drops(100000)),
2397  token::owner(carol));
2398  env.close();
2399  env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2400  env.close();
2401  carolBalance += drops(99999) - fee;
2402  beckyBalance -= drops(100000) + fee;
2403  aliceBalance += drops(1);
2404 
2405  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2406  BEAST_EXPECT(env.balance(minter) == minterBalance);
2407  BEAST_EXPECT(env.balance(carol) == carolBalance);
2408  BEAST_EXPECT(env.balance(becky) == beckyBalance);
2409  }
2410 
2411  // See the impact of rounding when the nft is sold for small amounts
2412  // of an IOU.
2413  {
2414  // An nft with a transfer fee of 1 basis point.
2415  uint256 const nftID =
2416  token::getNextID(env, alice, 0u, tfTransferable, 1);
2417  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2418  env.close();
2419 
2420  // Due to the floating point nature of IOUs we need to
2421  // significantly reduce the gwXAU balances of our accounts prior
2422  // to the iou transfer. Otherwise no transfers will happen.
2423  env(pay(alice, gw, env.balance(alice, gwXAU)));
2424  env(pay(minter, gw, env.balance(minter, gwXAU)));
2425  env(pay(becky, gw, env.balance(becky, gwXAU)));
2426  env.close();
2427 
2428  STAmount const startXAUBalance(
2429  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2430  env(pay(gw, alice, startXAUBalance));
2431  env(pay(gw, minter, startXAUBalance));
2432  env(pay(gw, becky, startXAUBalance));
2433  env.close();
2434 
2435  // Here is the smallest expressible gwXAU amount.
2436  STAmount tinyXAU(
2437  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset);
2438 
2439  // minter buys the nft for tinyXAU. Since the transfer involves
2440  // alice there should be no transfer fee.
2441  STAmount aliceBalance = env.balance(alice, gwXAU);
2442  STAmount minterBalance = env.balance(minter, gwXAU);
2443  uint256 const minterBuyOfferIndex =
2444  keylet::nftoffer(minter, env.seq(minter)).key;
2445  env(token::createOffer(minter, nftID, tinyXAU),
2446  token::owner(alice));
2447  env.close();
2448  env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2449  env.close();
2450  aliceBalance += tinyXAU;
2451  minterBalance -= tinyXAU;
2452  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2453  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2454 
2455  // minter sells to carol.
2456  STAmount carolBalance = env.balance(carol, gwXAU);
2457  uint256 const minterSellOfferIndex =
2458  keylet::nftoffer(minter, env.seq(minter)).key;
2459  env(token::createOffer(minter, nftID, tinyXAU),
2460  txflags(tfSellNFToken));
2461  env.close();
2462  env(token::acceptSellOffer(carol, minterSellOfferIndex));
2463  env.close();
2464 
2465  minterBalance += tinyXAU;
2466  carolBalance -= tinyXAU;
2467  // tiny XAU is so small that alice does not get a transfer fee.
2468  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2469  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2470  BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2471 
2472  // carol sells to becky. This is the smallest gwXAU amount
2473  // to pay for a transfer that enables a transfer fee of 1.
2474  STAmount const cheapNFT(
2475  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2476 
2477  STAmount beckyBalance = env.balance(becky, gwXAU);
2478  uint256 const beckyBuyOfferIndex =
2479  keylet::nftoffer(becky, env.seq(becky)).key;
2480  env(token::createOffer(becky, nftID, cheapNFT),
2481  token::owner(carol));
2482  env.close();
2483  env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2484  env.close();
2485 
2486  aliceBalance += tinyXAU;
2487  beckyBalance -= cheapNFT;
2488  carolBalance += cheapNFT - tinyXAU;
2489  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2490  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2491  BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2492  BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2493  }
2494  }
2495 
2496  void
2498  {
2499  // Exercise the NFT taxon field.
2500  testcase("Mint taxon");
2501 
2502  using namespace test::jtx;
2503 
2504  Env env{*this, features};
2505 
2506  Account const alice{"alice"};
2507  Account const becky{"becky"};
2508 
2509  env.fund(XRP(1000), alice, becky);
2510  env.close();
2511 
2512  // The taxon field is incorporated straight into the NFT ID. So
2513  // tests only need to operate on NFT IDs; we don't need to generate
2514  // any transactions.
2515 
2516  // The taxon value should be recoverable from the NFT ID.
2517  {
2518  uint256 const nftID = token::getNextID(env, alice, 0u);
2519  BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon(0));
2520  }
2521 
2522  // Make sure the full range of taxon values work. We just tried
2523  // the minimum. Now try the largest.
2524  {
2525  uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2526  BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon((0xFFFFFFFF)));
2527  }
2528 
2529  // Do some touch testing to show that the taxon is recoverable no
2530  // matter what else changes around it in the nft ID.
2531  {
2532  std::uint32_t const taxon = rand_int<std::uint32_t>();
2533  for (int i = 0; i < 10; ++i)
2534  {
2535  // lambda to produce a useful message on error.
2536  auto check = [this](std::uint32_t taxon, uint256 const& nftID) {
2537  nft::Taxon const gotTaxon = nft::getTaxon(nftID);
2538  if (nft::toTaxon(taxon) == gotTaxon)
2539  pass();
2540  else
2541  {
2542  std::stringstream ss;
2543  ss << "Taxon recovery failed from nftID "
2544  << to_string(nftID) << ". Expected: " << taxon
2545  << "; got: " << gotTaxon;
2546  fail(ss.str());
2547  }
2548  };
2549 
2550  uint256 const nftAliceID = token::getID(
2551  alice,
2552  taxon,
2553  rand_int<std::uint32_t>(),
2554  rand_int<std::uint16_t>(),
2555  rand_int<std::uint16_t>());
2556  check(taxon, nftAliceID);
2557 
2558  uint256 const nftBeckyID = token::getID(
2559  becky,
2560  taxon,
2561  rand_int<std::uint32_t>(),
2562  rand_int<std::uint16_t>(),
2563  rand_int<std::uint16_t>());
2564  check(taxon, nftBeckyID);
2565  }
2566  }
2567  }
2568 
2569  void
2571  {
2572  // Exercise the NFT URI field.
2573  // 1. Create a number of NFTs with and without URIs.
2574  // 2. Retrieve the NFTs from the server.
2575  // 3. Make sure the right URI is attached to each NFT.
2576  testcase("Mint URI");
2577 
2578  using namespace test::jtx;
2579 
2580  Env env{*this, features};
2581 
2582  Account const alice{"alice"};
2583  Account const becky{"becky"};
2584 
2585  env.fund(XRP(10000), alice, becky);
2586  env.close();
2587 
2588  // lambda that returns a randomly generated string which fits
2589  // the constraints of a URI. Empty strings may be returned.
2590  // In the empty string case do not add the URI to the nft.
2591  auto randURI = []() {
2592  std::string ret;
2593 
2594  // About 20% of the returned strings should be empty
2595  if (rand_int(4) == 0)
2596  return ret;
2597 
2598  std::size_t const strLen = rand_int(256);
2599  ret.reserve(strLen);
2600  for (std::size_t i = 0; i < strLen; ++i)
2601  ret.push_back(rand_byte());
2602 
2603  return ret;
2604  };
2605 
2606  // Make a list of URIs that we'll put in nfts.
2607  struct Entry
2608  {
2609  std::string uri;
2610  std::uint32_t taxon;
2611 
2612  Entry(std::string uri_, std::uint32_t taxon_)
2613  : uri(std::move(uri_)), taxon(taxon_)
2614  {
2615  }
2616  };
2617 
2618  std::vector<Entry> entries;
2619  entries.reserve(100);
2620  for (std::size_t i = 0; i < 100; ++i)
2621  entries.emplace_back(randURI(), rand_int<std::uint32_t>());
2622 
2623  // alice creates nfts using entries.
2624  for (Entry const& entry : entries)
2625  {
2626  if (entry.uri.empty())
2627  {
2628  env(token::mint(alice, entry.taxon));
2629  }
2630  else
2631  {
2632  env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2633  }
2634  env.close();
2635  }
2636 
2637  // Recover alice's nfts from the ledger.
2638  Json::Value aliceNFTs = [&env, &alice]() {
2639  Json::Value params;
2640  params[jss::account] = alice.human();
2641  params[jss::type] = "state";
2642  return env.rpc("json", "account_nfts", to_string(params));
2643  }();
2644 
2645  // Verify that the returned NFTs match what we sent.
2646  Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2647  if (!BEAST_EXPECT(nfts.size() == entries.size()))
2648  return;
2649 
2650  // Sort the returned NFTs by nft_serial so the are in the same order
2651  // as entries.
2652  std::vector<Json::Value> sortedNFTs;
2653  sortedNFTs.reserve(nfts.size());
2654  for (std::size_t i = 0; i < nfts.size(); ++i)
2655  sortedNFTs.push_back(nfts[i]);
2656  std::sort(
2657  sortedNFTs.begin(),
2658  sortedNFTs.end(),
2659  [](Json::Value const& lhs, Json::Value const& rhs) {
2660  return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2661  });
2662 
2663  for (std::size_t i = 0; i < entries.size(); ++i)
2664  {
2665  Entry const& entry = entries[i];
2666  Json::Value const& ret = sortedNFTs[i];
2667  BEAST_EXPECT(entry.taxon == ret[sfNFTokenTaxon.jsonName]);
2668  if (entry.uri.empty())
2669  {
2670  BEAST_EXPECT(!ret.isMember(sfURI.jsonName));
2671  }
2672  else
2673  {
2674  BEAST_EXPECT(strHex(entry.uri) == ret[sfURI.jsonName]);
2675  }
2676  }
2677  }
2678 
2679  void
2681  {
2682  // Explore the CreateOffer Destination field.
2683  testcase("Create offer destination");
2684 
2685  using namespace test::jtx;
2686 
2687  Env env{*this, features};
2688 
2689  Account const issuer{"issuer"};
2690  Account const minter{"minter"};
2691  Account const buyer{"buyer"};
2692  Account const broker{"broker"};
2693 
2694  env.fund(XRP(1000), issuer, minter, buyer, broker);
2695 
2696  // We want to explore how issuers vs minters fits into the permission
2697  // scheme. So issuer issues and minter mints.
2698  env(token::setMinter(issuer, minter));
2699  env.close();
2700 
2701  uint256 const nftokenID =
2702  token::getNextID(env, issuer, 0, tfTransferable);
2703  env(token::mint(minter, 0),
2704  token::issuer(issuer),
2705  txflags(tfTransferable));
2706  env.close();
2707 
2708  // Test how adding a Destination field to an offer affects permissions
2709  // for canceling offers.
2710  {
2711  uint256 const offerMinterToIssuer =
2712  keylet::nftoffer(minter, env.seq(minter)).key;
2713  env(token::createOffer(minter, nftokenID, drops(1)),
2714  token::destination(issuer),
2715  txflags(tfSellNFToken));
2716 
2717  uint256 const offerMinterToBuyer =
2718  keylet::nftoffer(minter, env.seq(minter)).key;
2719  env(token::createOffer(minter, nftokenID, drops(1)),
2720  token::destination(buyer),
2721  txflags(tfSellNFToken));
2722 
2723  uint256 const offerIssuerToMinter =
2724  keylet::nftoffer(issuer, env.seq(issuer)).key;
2725  env(token::createOffer(issuer, nftokenID, drops(1)),
2726  token::owner(minter),
2727  token::destination(minter));
2728 
2729  uint256 const offerIssuerToBuyer =
2730  keylet::nftoffer(issuer, env.seq(issuer)).key;
2731  env(token::createOffer(issuer, nftokenID, drops(1)),
2732  token::owner(minter),
2733  token::destination(buyer));
2734 
2735  env.close();
2736  BEAST_EXPECT(ownerCount(env, issuer) == 2);
2737  BEAST_EXPECT(ownerCount(env, minter) == 3);
2738  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2739 
2740  // Test who gets to cancel the offers. Anyone outside of the
2741  // offer-owner/destination pair should not be able to cancel the
2742  // offers.
2743  //
2744  // Note that issuer does not have any special permissions regarding
2745  // offer cancellation. issuer cannot cancel an offer for an
2746  // NFToken they issued.
2747  env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2748  ter(tecNO_PERMISSION));
2749  env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2750  ter(tecNO_PERMISSION));
2751  env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2752  ter(tecNO_PERMISSION));
2753  env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2754  ter(tecNO_PERMISSION));
2755  env.close();
2756  BEAST_EXPECT(ownerCount(env, issuer) == 2);
2757  BEAST_EXPECT(ownerCount(env, minter) == 3);
2758  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2759 
2760  // Both the offer creator and and destination should be able to
2761  // cancel the offers.
2762  env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2763  env(token::cancelOffer(minter, {offerMinterToIssuer}));
2764  env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2765  env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2766  env.close();
2767  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2768  BEAST_EXPECT(ownerCount(env, minter) == 1);
2769  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2770  }
2771 
2772  // Test how adding a Destination field to a sell offer affects
2773  // accepting that offer.
2774  {
2775  uint256 const offerMinterSellsToBuyer =
2776  keylet::nftoffer(minter, env.seq(minter)).key;
2777  env(token::createOffer(minter, nftokenID, drops(1)),
2778  token::destination(buyer),
2779  txflags(tfSellNFToken));
2780  env.close();
2781  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2782  BEAST_EXPECT(ownerCount(env, minter) == 2);
2783  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2784 
2785  // issuer cannot accept a sell offer where they are not the
2786  // destination.
2787  env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2788  ter(tecNO_PERMISSION));
2789  env.close();
2790  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2791  BEAST_EXPECT(ownerCount(env, minter) == 2);
2792  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2793 
2794  // However buyer can accept the sell offer.
2795  env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2796  env.close();
2797  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2798  BEAST_EXPECT(ownerCount(env, minter) == 0);
2799  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2800  }
2801 
2802  // Test how adding a Destination field to a buy offer affects
2803  // accepting that offer.
2804  {
2805  uint256 const offerMinterBuysFromBuyer =
2806  keylet::nftoffer(minter, env.seq(minter)).key;
2807  env(token::createOffer(minter, nftokenID, drops(1)),
2808  token::owner(buyer),
2809  token::destination(buyer));
2810  env.close();
2811  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2812  BEAST_EXPECT(ownerCount(env, minter) == 1);
2813  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2814 
2815  // issuer cannot accept a buy offer where they are the
2816  // destination.
2817  env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2818  ter(tecNO_PERMISSION));
2819  env.close();
2820  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2821  BEAST_EXPECT(ownerCount(env, minter) == 1);
2822  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2823 
2824  // Buyer accepts minter's offer.
2825  env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2826  env.close();
2827  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2828  BEAST_EXPECT(ownerCount(env, minter) == 1);
2829  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2830 
2831  // If a destination other than the NFToken owner is set, that
2832  // destination must act as a broker. The NFToken owner may not
2833  // simply accept the offer.
2834  uint256 const offerBuyerBuysFromMinter =
2835  keylet::nftoffer(buyer, env.seq(buyer)).key;
2836  env(token::createOffer(buyer, nftokenID, drops(1)),
2837  token::owner(minter),
2838  token::destination(broker));
2839  env.close();
2840  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2841  BEAST_EXPECT(ownerCount(env, minter) == 1);
2842  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2843 
2844  env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2845  ter(tecNO_PERMISSION));
2846  env.close();
2847 
2848  // Clean up the unused offer.
2849  env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2850  env.close();
2851  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2852  BEAST_EXPECT(ownerCount(env, minter) == 1);
2853  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2854  }
2855 
2856  // Show that a sell offer's Destination can broker that sell offer
2857  // to another account.
2858  {
2859  uint256 const offerMinterToBroker =
2860  keylet::nftoffer(minter, env.seq(minter)).key;
2861  env(token::createOffer(minter, nftokenID, drops(1)),
2862  token::destination(broker),
2863  txflags(tfSellNFToken));
2864 
2865  uint256 const offerBuyerToMinter =
2866  keylet::nftoffer(buyer, env.seq(buyer)).key;
2867  env(token::createOffer(buyer, nftokenID, drops(1)),
2868  token::owner(minter));
2869 
2870  env.close();
2871  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2872  BEAST_EXPECT(ownerCount(env, minter) == 2);
2873  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2874 
2875  // issuer cannot broker the offers, because they are not the
2876  // Destination.
2877  env(token::brokerOffers(
2878  issuer, offerBuyerToMinter, offerMinterToBroker),
2880  env.close();
2881  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2882  BEAST_EXPECT(ownerCount(env, minter) == 2);
2883  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2884 
2885  // Since broker is the sell offer's destination, they can broker
2886  // the two offers.
2887  env(token::brokerOffers(
2888  broker, offerBuyerToMinter, offerMinterToBroker));
2889  env.close();
2890  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2891  BEAST_EXPECT(ownerCount(env, minter) == 0);
2892  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2893  }
2894 
2895  // Show that brokered mode cannot complete a transfer where the
2896  // Destination doesn't match, but can complete if the Destination
2897  // does match.
2898  {
2899  uint256 const offerBuyerToMinter =
2900  keylet::nftoffer(buyer, env.seq(buyer)).key;
2901  env(token::createOffer(buyer, nftokenID, drops(1)),
2902  token::destination(minter),
2903  txflags(tfSellNFToken));
2904 
2905  uint256 const offerMinterToBuyer =
2906  keylet::nftoffer(minter, env.seq(minter)).key;
2907  env(token::createOffer(minter, nftokenID, drops(1)),
2908  token::owner(buyer));
2909 
2910  uint256 const offerIssuerToBuyer =
2911  keylet::nftoffer(issuer, env.seq(issuer)).key;
2912  env(token::createOffer(issuer, nftokenID, drops(1)),
2913  token::owner(buyer));
2914 
2915  env.close();
2916  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2917  BEAST_EXPECT(ownerCount(env, minter) == 1);
2918  BEAST_EXPECT(ownerCount(env, buyer) == 2);
2919 
2920  // Cannot broker offers when the sell destination is not the buyer.
2921  env(token::brokerOffers(
2922  broker, offerIssuerToBuyer, offerBuyerToMinter),
2924  env.close();
2925  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2926  BEAST_EXPECT(ownerCount(env, minter) == 1);
2927  BEAST_EXPECT(ownerCount(env, buyer) == 2);
2928 
2929  // Broker is successful when destination is buyer.
2930  env(token::brokerOffers(
2931  broker, offerMinterToBuyer, offerBuyerToMinter));
2932  env.close();
2933  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2934  BEAST_EXPECT(ownerCount(env, minter) == 1);
2935  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2936 
2937  // Clean out the unconsumed offer.
2938  env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
2939  env.close();
2940  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2941  BEAST_EXPECT(ownerCount(env, minter) == 1);
2942  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2943  }
2944 
2945  // Show that if a buy and a sell offer both have the same destination,
2946  // then that destination can broker the offers.
2947  {
2948  uint256 const offerMinterToBroker =
2949  keylet::nftoffer(minter, env.seq(minter)).key;
2950  env(token::createOffer(minter, nftokenID, drops(1)),
2951  token::destination(broker),
2952  txflags(tfSellNFToken));
2953 
2954  uint256 const offerBuyerToBroker =
2955  keylet::nftoffer(buyer, env.seq(buyer)).key;
2956  env(token::createOffer(buyer, nftokenID, drops(1)),
2957  token::owner(minter),
2958  token::destination(broker));
2959 
2960  // Cannot broker offers when the sell destination is not the buyer
2961  // or the broker.
2962  env(token::brokerOffers(
2963  issuer, offerBuyerToBroker, offerMinterToBroker),
2965  env.close();
2966  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2967  BEAST_EXPECT(ownerCount(env, minter) == 2);
2968  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2969 
2970  // Broker is successful if they are the destination of both offers.
2971  env(token::brokerOffers(
2972  broker, offerBuyerToBroker, offerMinterToBroker));
2973  env.close();
2974  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2975  BEAST_EXPECT(ownerCount(env, minter) == 0);
2976  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2977  }
2978  }
2979 
2980  void
2982  {
2983  testcase("Create offer destination disallow incoming");
2984 
2985  using namespace test::jtx;
2986 
2987  // test flag doesn't set unless amendment enabled
2988  {
2989  Env env{*this, features - disallowIncoming};
2990  Account const alice{"alice"};
2991  env.fund(XRP(10000), alice);
2992  env(fset(alice, asfDisallowIncomingNFTOffer));
2993  env.close();
2994  auto const sle = env.le(alice);
2995  uint32_t flags = sle->getFlags();
2996  BEAST_EXPECT(!(flags & lsfDisallowIncomingNFTOffer));
2997  }
2998 
2999  Env env{*this, features | disallowIncoming};
3000 
3001  Account const issuer{"issuer"};
3002  Account const minter{"minter"};
3003  Account const buyer{"buyer"};
3004  Account const alice{"alice"};
3005 
3006  env.fund(XRP(1000), issuer, minter, buyer, alice);
3007 
3008  env(token::setMinter(issuer, minter));
3009  env.close();
3010 
3011  uint256 const nftokenID =
3012  token::getNextID(env, issuer, 0, tfTransferable);
3013  env(token::mint(minter, 0),
3014  token::issuer(issuer),
3015  txflags(tfTransferable));
3016  env.close();
3017 
3018  // enable flag
3019  env(fset(buyer, asfDisallowIncomingNFTOffer));
3020  env.close();
3021 
3022  // a sell offer from the minter to the buyer should be rejected
3023  {
3024  env(token::createOffer(minter, nftokenID, drops(1)),
3025  token::destination(buyer),
3026  txflags(tfSellNFToken),
3027  ter(tecNO_PERMISSION));
3028  env.close();
3029  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3030  BEAST_EXPECT(ownerCount(env, minter) == 1);
3031  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3032  }
3033 
3034  // disable the flag
3035  env(fclear(buyer, asfDisallowIncomingNFTOffer));
3036  env.close();
3037 
3038  // create offer (allowed now) then cancel
3039  {
3040  uint256 const offerIndex =
3041  keylet::nftoffer(minter, env.seq(minter)).key;
3042 
3043  env(token::createOffer(minter, nftokenID, drops(1)),
3044  token::destination(buyer),
3045  txflags(tfSellNFToken));
3046  env.close();
3047 
3048  env(token::cancelOffer(minter, {offerIndex}));
3049  env.close();
3050  }
3051 
3052  // create offer, enable flag, then cancel
3053  {
3054  uint256 const offerIndex =
3055  keylet::nftoffer(minter, env.seq(minter)).key;
3056 
3057  env(token::createOffer(minter, nftokenID, drops(1)),
3058  token::destination(buyer),
3059  txflags(tfSellNFToken));
3060  env.close();
3061 
3062  env(fset(buyer, asfDisallowIncomingNFTOffer));
3063  env.close();
3064 
3065  env(token::cancelOffer(minter, {offerIndex}));
3066  env.close();
3067 
3068  env(fclear(buyer, asfDisallowIncomingNFTOffer));
3069  env.close();
3070  }
3071 
3072  // create offer then transfer
3073  {
3074  uint256 const offerIndex =
3075  keylet::nftoffer(minter, env.seq(minter)).key;
3076 
3077  env(token::createOffer(minter, nftokenID, drops(1)),
3078  token::destination(buyer),
3079  txflags(tfSellNFToken));
3080  env.close();
3081 
3082  env(token::acceptSellOffer(buyer, offerIndex));
3083  env.close();
3084  }
3085 
3086  // buyer now owns the token
3087 
3088  // enable flag again
3089  env(fset(buyer, asfDisallowIncomingNFTOffer));
3090  env.close();
3091 
3092  // a random offer to buy the token
3093  {
3094  env(token::createOffer(alice, nftokenID, drops(1)),
3095  token::owner(buyer),
3096  ter(tecNO_PERMISSION));
3097  env.close();
3098  }
3099 
3100  // minter offer to buy the token
3101  {
3102  env(token::createOffer(minter, nftokenID, drops(1)),
3103  token::owner(buyer),
3104  ter(tecNO_PERMISSION));
3105  env.close();
3106  }
3107  }
3108 
3109  void
3111  {
3112  // Explore the CreateOffer Expiration field.
3113  testcase("Create offer expiration");
3114 
3115  using namespace test::jtx;
3116 
3117  Env env{*this, features};
3118 
3119  Account const issuer{"issuer"};
3120  Account const minter{"minter"};
3121  Account const buyer{"buyer"};
3122 
3123  env.fund(XRP(1000), issuer, minter, buyer);
3124 
3125  // We want to explore how issuers vs minters fits into the permission
3126  // scheme. So issuer issues and minter mints.
3127  env(token::setMinter(issuer, minter));
3128  env.close();
3129 
3130  uint256 const nftokenID0 =
3131  token::getNextID(env, issuer, 0, tfTransferable);
3132  env(token::mint(minter, 0),
3133  token::issuer(issuer),
3134  txflags(tfTransferable));
3135  env.close();
3136 
3137  uint256 const nftokenID1 =
3138  token::getNextID(env, issuer, 0, tfTransferable);
3139  env(token::mint(minter, 0),
3140  token::issuer(issuer),
3141  txflags(tfTransferable));
3142  env.close();
3143 
3144  // Test how adding an Expiration field to an offer affects permissions
3145  // for cancelling offers.
3146  {
3147  std::uint32_t const expiration = lastClose(env) + 25;
3148 
3149  uint256 const offerMinterToIssuer =
3150  keylet::nftoffer(minter, env.seq(minter)).key;
3151  env(token::createOffer(minter, nftokenID0, drops(1)),
3152  token::destination(issuer),
3153  token::expiration(expiration),
3154  txflags(tfSellNFToken));
3155 
3156  uint256 const offerMinterToAnyone =
3157  keylet::nftoffer(minter, env.seq(minter)).key;
3158  env(token::createOffer(minter, nftokenID0, drops(1)),
3159  token::expiration(expiration),
3160  txflags(tfSellNFToken));
3161 
3162  uint256 const offerIssuerToMinter =
3163  keylet::nftoffer(issuer, env.seq(issuer)).key;
3164  env(token::createOffer(issuer, nftokenID0, drops(1)),
3165  token::owner(minter),
3166  token::expiration(expiration));
3167 
3168  uint256 const offerBuyerToMinter =
3169  keylet::nftoffer(buyer, env.seq(buyer)).key;
3170  env(token::createOffer(buyer, nftokenID0, drops(1)),
3171  token::owner(minter),
3172  token::expiration(expiration));
3173  env.close();
3174  BEAST_EXPECT(ownerCount(env, issuer) == 1);
3175  BEAST_EXPECT(ownerCount(env, minter) == 3);
3176  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3177 
3178  // Test who gets to cancel the offers. Anyone outside of the
3179  // offer-owner/destination pair should not be able to cancel
3180  // unexpired offers.
3181  //
3182  // Note that these are tec responses, so these transactions will
3183  // not be retried by the ledger.
3184  env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3185  ter(tecNO_PERMISSION));
3186  env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3187  ter(tecNO_PERMISSION));
3188  env.close();
3189  BEAST_EXPECT(lastClose(env) < expiration);
3190  BEAST_EXPECT(ownerCount(env, issuer) == 1);
3191  BEAST_EXPECT(ownerCount(env, minter) == 3);
3192  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3193 
3194  // The offer creator can cancel their own unexpired offer.
3195  env(token::cancelOffer(minter, {offerMinterToAnyone}));
3196 
3197  // The destination of a sell offer can cancel the NFT owner's
3198  // unexpired offer.
3199  env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3200 
3201  // Close enough ledgers to get past the expiration.
3202  while (lastClose(env) < expiration)
3203  env.close();
3204 
3205  BEAST_EXPECT(ownerCount(env, issuer) == 1);
3206  BEAST_EXPECT(ownerCount(env, minter) == 1);
3207  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3208 
3209  // Anyone can cancel expired offers.
3210  env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3211  env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3212  env.close();
3213  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3214  BEAST_EXPECT(ownerCount(env, minter) == 1);
3215  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3216  }
3217  // Show that:
3218  // 1. An unexpired sell offer with an expiration can be accepted.
3219  // 2. An expired sell offer cannot be accepted and remains
3220  // in ledger after the accept fails.
3221  {
3222  std::uint32_t const expiration = lastClose(env) + 25;
3223 
3224  uint256 const offer0 =
3225  keylet::nftoffer(minter, env.seq(minter)).key;
3226  env(token::createOffer(minter, nftokenID0, drops(1)),
3227  token::expiration(expiration),
3228  txflags(tfSellNFToken));
3229 
3230  uint256 const offer1 =
3231  keylet::nftoffer(minter, env.seq(minter)).key;
3232  env(token::createOffer(minter, nftokenID1, drops(1)),
3233  token::expiration(expiration),
3234  txflags(tfSellNFToken));
3235  env.close();
3236  BEAST_EXPECT(lastClose(env) < expiration);
3237  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3238  BEAST_EXPECT(ownerCount(env, minter) == 3);
3239  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3240 
3241  // Anyone can accept an unexpired sell offer.
3242  env(token::acceptSellOffer(buyer, offer0));
3243 
3244  // Close enough ledgers to get past the expiration.
3245  while (lastClose(env) < expiration)
3246  env.close();
3247 
3248  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3249  BEAST_EXPECT(ownerCount(env, minter) == 2);
3250  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3251 
3252  // No one can accept an expired sell offer.
3253  env(token::acceptSellOffer(buyer, offer1), ter(tecEXPIRED));
3254  env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
3255  env.close();
3256 
3257  // The expired sell offer is still in the ledger.
3258  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3259  BEAST_EXPECT(ownerCount(env, minter) == 2);
3260  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3261 
3262  // Anyone can cancel the expired sell offer.
3263  env(token::cancelOffer(issuer, {offer1}));
3264  env.close();
3265  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3266  BEAST_EXPECT(ownerCount(env, minter) == 1);
3267  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3268 
3269  // Transfer nftokenID0 back to minter so we start the next test in
3270  // a simple place.
3271  uint256 const offerSellBack =
3272  keylet::nftoffer(buyer, env.seq(buyer)).key;
3273  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3274  txflags(tfSellNFToken),
3275  token::destination(minter));
3276  env.close();
3277  env(token::acceptSellOffer(minter, offerSellBack));
3278  env.close();
3279  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3280  BEAST_EXPECT(ownerCount(env, minter) == 1);
3281  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3282  }
3283  // Show that:
3284  // 1. An unexpired buy offer with an expiration can be accepted.
3285  // 2. An expired buy offer cannot be accepted and remains
3286  // in ledger after the accept fails.
3287  {
3288  std::uint32_t const expiration = lastClose(env) + 25;
3289 
3290  uint256 const offer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3291  env(token::createOffer(buyer, nftokenID0, drops(1)),
3292  token::owner(minter),
3293  token::expiration(expiration));
3294 
3295  uint256 const offer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3296  env(token::createOffer(buyer, nftokenID1, drops(1)),
3297  token::owner(minter),
3298  token::expiration(expiration));
3299  env.close();
3300  BEAST_EXPECT(lastClose(env) < expiration);
3301  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3302  BEAST_EXPECT(ownerCount(env, minter) == 1);
3303  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3304 
3305  // An unexpired buy offer can be accepted.
3306  env(token::acceptBuyOffer(minter, offer0));
3307 
3308  // Close enough ledgers to get past the expiration.
3309  while (lastClose(env) < expiration)
3310  env.close();
3311 
3312  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3313  BEAST_EXPECT(ownerCount(env, minter) == 1);
3314  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3315 
3316  // An expired buy offer cannot be accepted.
3317  env(token::acceptBuyOffer(minter, offer1), ter(tecEXPIRED));
3318  env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
3319  env.close();
3320 
3321  // The expired buy offer is still in the ledger.
3322  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3323  BEAST_EXPECT(ownerCount(env, minter) == 1);
3324  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3325 
3326  // Anyone can cancel the expired buy offer.
3327  env(token::cancelOffer(issuer, {offer1}));
3328  env.close();
3329  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3330  BEAST_EXPECT(ownerCount(env, minter) == 1);
3331  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3332 
3333  // Transfer nftokenID0 back to minter so we start the next test in
3334  // a simple place.
3335  uint256 const offerSellBack =
3336  keylet::nftoffer(buyer, env.seq(buyer)).key;
3337  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3338  txflags(tfSellNFToken),
3339  token::destination(minter));
3340  env.close();
3341  env(token::acceptSellOffer(minter, offerSellBack));
3342  env.close();
3343  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3344  BEAST_EXPECT(ownerCount(env, minter) == 1);
3345  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3346  }
3347  // Show that in brokered mode:
3348  // 1. An unexpired sell offer with an expiration can be accepted.
3349  // 2. An expired sell offer cannot be accepted and remains
3350  // in ledger after the accept fails.
3351  {
3352  std::uint32_t const expiration = lastClose(env) + 25;
3353 
3354  uint256 const sellOffer0 =
3355  keylet::nftoffer(minter, env.seq(minter)).key;
3356  env(token::createOffer(minter, nftokenID0, drops(1)),
3357  token::expiration(expiration),
3358  txflags(tfSellNFToken));
3359 
3360  uint256 const sellOffer1 =
3361  keylet::nftoffer(minter, env.seq(minter)).key;
3362  env(token::createOffer(minter, nftokenID1, drops(1)),
3363  token::expiration(expiration),
3364  txflags(tfSellNFToken));
3365 
3366  uint256 const buyOffer0 =
3367  keylet::nftoffer(buyer, env.seq(buyer)).key;
3368  env(token::createOffer(buyer, nftokenID0, drops(1)),
3369  token::owner(minter));
3370 
3371  uint256 const buyOffer1 =
3372  keylet::nftoffer(buyer, env.seq(buyer)).key;
3373  env(token::createOffer(buyer, nftokenID1, drops(1)),
3374  token::owner(minter));
3375 
3376  env.close();
3377  BEAST_EXPECT(lastClose(env) < expiration);
3378  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3379  BEAST_EXPECT(ownerCount(env, minter) == 3);
3380  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3381 
3382  // An unexpired offer can be brokered.
3383  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3384 
3385  // Close enough ledgers to get past the expiration.
3386  while (lastClose(env) < expiration)
3387  env.close();
3388 
3389  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3390  BEAST_EXPECT(ownerCount(env, minter) == 2);
3391  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3392 
3393  // If the sell offer is expired it cannot be brokered.
3394  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3395  ter(tecEXPIRED));
3396  env.close();
3397 
3398  // The expired sell offer is still in the ledger.
3399  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3400  BEAST_EXPECT(ownerCount(env, minter) == 2);
3401  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3402 
3403  // Anyone can cancel the expired sell offer.
3404  env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3405  env.close();
3406  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3407  BEAST_EXPECT(ownerCount(env, minter) == 1);
3408  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3409 
3410  // Transfer nftokenID0 back to minter so we start the next test in
3411  // a simple place.
3412  uint256 const offerSellBack =
3413  keylet::nftoffer(buyer, env.seq(buyer)).key;
3414  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3415  txflags(tfSellNFToken),
3416  token::destination(minter));
3417  env.close();
3418  env(token::acceptSellOffer(minter, offerSellBack));
3419  env.close();
3420  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3421  BEAST_EXPECT(ownerCount(env, minter) == 1);
3422  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3423  }
3424  // Show that in brokered mode:
3425  // 1. An unexpired buy offer with an expiration can be accepted.
3426  // 2. An expired buy offer cannot be accepted and remains
3427  // in ledger after the accept fails.
3428  {
3429  std::uint32_t const expiration = lastClose(env) + 25;
3430 
3431  uint256 const sellOffer0 =
3432  keylet::nftoffer(minter, env.seq(minter)).key;
3433  env(token::createOffer(minter, nftokenID0, drops(1)),
3434  txflags(tfSellNFToken));
3435 
3436  uint256 const sellOffer1 =
3437  keylet::nftoffer(minter, env.seq(minter)).key;
3438  env(token::createOffer(minter, nftokenID1, drops(1)),
3439  txflags(tfSellNFToken));
3440 
3441  uint256 const buyOffer0 =
3442  keylet::nftoffer(buyer, env.seq(buyer)).key;
3443  env(token::createOffer(buyer, nftokenID0, drops(1)),
3444  token::expiration(expiration),
3445  token::owner(minter));
3446 
3447  uint256 const buyOffer1 =
3448  keylet::nftoffer(buyer, env.seq(buyer)).key;
3449  env(token::createOffer(buyer, nftokenID1, drops(1)),
3450  token::expiration(expiration),
3451  token::owner(minter));
3452 
3453  env.close();
3454  BEAST_EXPECT(lastClose(env) < expiration);
3455  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3456  BEAST_EXPECT(ownerCount(env, minter) == 3);
3457  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3458 
3459  // An unexpired offer can be brokered.
3460  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3461 
3462  // Close enough ledgers to get past the expiration.
3463  while (lastClose(env) < expiration)
3464  env.close();
3465 
3466  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3467  BEAST_EXPECT(ownerCount(env, minter) == 2);
3468  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3469 
3470  // If the buy offer is expired it cannot be brokered.
3471  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3472  ter(tecEXPIRED));
3473  env.close();
3474 
3475  // The expired buy offer is still in the ledger.
3476  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3477  BEAST_EXPECT(ownerCount(env, minter) == 2);
3478  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3479 
3480  // Anyone can cancel the expired buy offer.
3481  env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3482  env.close();
3483  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3484  BEAST_EXPECT(ownerCount(env, minter) == 1);
3485  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3486 
3487  // Transfer nftokenID0 back to minter so we start the next test in
3488  // a simple place.
3489  uint256 const offerSellBack =
3490  keylet::nftoffer(buyer, env.seq(buyer)).key;
3491  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3492  txflags(tfSellNFToken),
3493  token::destination(minter));
3494  env.close();
3495  env(token::acceptSellOffer(minter, offerSellBack));
3496  env.close();
3497  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3498  BEAST_EXPECT(ownerCount(env, minter) == 1);
3499  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3500  }
3501  // Show that in brokered mode:
3502  // 1. An unexpired buy/sell offer pair with an expiration can be
3503  // accepted.
3504  // 2. An expired buy/sell offer pair cannot be accepted and they
3505  // remain in ledger after the accept fails.
3506  {
3507  std::uint32_t const expiration = lastClose(env) + 25;
3508 
3509  uint256 const sellOffer0 =
3510  keylet::nftoffer(minter, env.seq(minter)).key;
3511  env(token::createOffer(minter, nftokenID0, drops(1)),
3512  token::expiration(expiration),
3513  txflags(tfSellNFToken));
3514 
3515  uint256 const sellOffer1 =
3516  keylet::nftoffer(minter, env.seq(minter)).key;
3517  env(token::createOffer(minter, nftokenID1, drops(1)),
3518  token::expiration(expiration),
3519  txflags(tfSellNFToken));
3520 
3521  uint256 const buyOffer0 =
3522  keylet::nftoffer(buyer, env.seq(buyer)).key;
3523  env(token::createOffer(buyer, nftokenID0, drops(1)),
3524  token::expiration(expiration),
3525  token::owner(minter));
3526 
3527  uint256 const buyOffer1 =
3528  keylet::nftoffer(buyer, env.seq(buyer)).key;
3529  env(token::createOffer(buyer, nftokenID1, drops(1)),
3530  token::expiration(expiration),
3531  token::owner(minter));
3532 
3533  env.close();
3534  BEAST_EXPECT(lastClose(env) < expiration);
3535  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3536  BEAST_EXPECT(ownerCount(env, minter) == 3);
3537  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3538 
3539  // Unexpired offers can be brokered.
3540  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3541 
3542  // Close enough ledgers to get past the expiration.
3543  while (lastClose(env) < expiration)
3544  env.close();
3545 
3546  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3547  BEAST_EXPECT(ownerCount(env, minter) == 2);
3548  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3549 
3550  // If the offers are expired they cannot be brokered.
3551  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3552  ter(tecEXPIRED));
3553  env.close();
3554 
3555  // The expired offers are still in the ledger.
3556  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3557  BEAST_EXPECT(ownerCount(env, minter) == 2);
3558  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3559 
3560  // Anyone can cancel the expired offers.
3561  env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3562  env.close();
3563  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3564  BEAST_EXPECT(ownerCount(env, minter) == 1);
3565  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3566 
3567  // Transfer nftokenID0 back to minter so we start the next test in
3568  // a simple place.
3569  uint256 const offerSellBack =
3570  keylet::nftoffer(buyer, env.seq(buyer)).key;
3571  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3572  txflags(tfSellNFToken),
3573  token::destination(minter));
3574  env.close();
3575  env(token::acceptSellOffer(minter, offerSellBack));
3576  env.close();
3577  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3578  BEAST_EXPECT(ownerCount(env, minter) == 1);
3579  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3580  }
3581  }
3582 
3583  void
3585  {
3586  // Look at offer canceling.
3587  testcase("Cancel offers");
3588 
3589  using namespace test::jtx;
3590 
3591  Env env{*this, features};
3592 
3593  Account const alice("alice");
3594  Account const becky("becky");
3595  Account const minter("minter");
3596  env.fund(XRP(50000), alice, becky, minter);
3597  env.close();
3598 
3599  // alice has a minter to see if minters have offer canceling permission.
3600  env(token::setMinter(alice, minter));
3601  env.close();
3602 
3603  uint256 const nftokenID =
3604  token::getNextID(env, alice, 0, tfTransferable);
3605  env(token::mint(alice, 0), txflags(tfTransferable));
3606  env.close();
3607 
3608  // Anyone can cancel an expired offer.
3609  uint256 const expiredOfferIndex =
3610  keylet::nftoffer(alice, env.seq(alice)).key;
3611 
3612  env(token::createOffer(alice, nftokenID, XRP(1000)),
3613  txflags(tfSellNFToken),
3614  token::expiration(lastClose(env) + 13));
3615  env.close();
3616 
3617  // The offer has not expired yet, so becky can't cancel it now.
3618  BEAST_EXPECT(ownerCount(env, alice) == 2);
3619  env(token::cancelOffer(becky, {expiredOfferIndex}),
3620  ter(tecNO_PERMISSION));
3621  env.close();
3622 
3623  // Close a couple of ledgers and advance the time. Then becky
3624  // should be able to cancel the (now) expired offer.
3625  env.close();
3626  env.close();
3627  env(token::cancelOffer(becky, {expiredOfferIndex}));
3628  env.close();
3629  BEAST_EXPECT(ownerCount(env, alice) == 1);
3630 
3631  // Create a couple of offers with a destination. Those offers
3632  // should be cancellable by the creator and the destination.
3633  uint256 const dest1OfferIndex =
3634  keylet::nftoffer(alice, env.seq(alice)).key;
3635 
3636  env(token::createOffer(alice, nftokenID, XRP(1000)),
3637  token::destination(becky),
3638  txflags(tfSellNFToken));
3639  env.close();
3640  BEAST_EXPECT(ownerCount(env, alice) == 2);
3641 
3642  // Minter can't cancel that offer, but becky (the destination) can.
3643  env(token::cancelOffer(minter, {dest1OfferIndex}),
3644  ter(tecNO_PERMISSION));
3645  env.close();
3646  BEAST_EXPECT(ownerCount(env, alice) == 2);
3647 
3648  env(token::cancelOffer(becky, {dest1OfferIndex}));
3649  env.close();
3650  BEAST_EXPECT(ownerCount(env, alice) == 1);
3651 
3652  // alice can cancel her own offer, even if becky is the destination.
3653  uint256 const dest2OfferIndex =
3654  keylet::nftoffer(alice, env.seq(alice)).key;
3655 
3656  env(token::createOffer(alice, nftokenID, XRP(1000)),
3657  token::destination(becky),
3658  txflags(tfSellNFToken));
3659  env.close();
3660  BEAST_EXPECT(ownerCount(env, alice) == 2);
3661 
3662  env(token::cancelOffer(alice, {dest2OfferIndex}));
3663  env.close();
3664  BEAST_EXPECT(ownerCount(env, alice) == 1);
3665 
3666  // The issuer has no special permissions regarding offer cancellation.
3667  // Minter creates a token with alice as issuer. alice cannot cancel
3668  // minter's offer.
3669  uint256 const mintersNFTokenID =
3670  token::getNextID(env, alice, 0, tfTransferable);
3671  env(token::mint(minter, 0),
3672  token::issuer(alice),
3673  txflags(tfTransferable));
3674  env.close();
3675 
3676  uint256 const minterOfferIndex =
3677  keylet::nftoffer(minter, env.seq(minter)).key;
3678 
3679  env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3680  txflags(tfSellNFToken));
3681  env.close();
3682  BEAST_EXPECT(ownerCount(env, minter) == 2);
3683 
3684  // Nobody other than minter should be able to cancel minter's offer.
3685  env(token::cancelOffer(alice, {minterOfferIndex}),
3686  ter(tecNO_PERMISSION));
3687  env(token::cancelOffer(becky, {minterOfferIndex}),
3688  ter(tecNO_PERMISSION));
3689  env.close();
3690  BEAST_EXPECT(ownerCount(env, minter) == 2);
3691 
3692  env(token::cancelOffer(minter, {minterOfferIndex}));
3693  env.close();
3694  BEAST_EXPECT(ownerCount(env, minter) == 1);
3695  }
3696 
3697  void
3699  {
3700  // Look at the case where too many offers are passed in a cancel.
3701  testcase("Cancel too many offers");
3702 
3703  using namespace test::jtx;
3704 
3705  Env env{*this, features};
3706 
3707  // We want to maximize the metadata from a cancel offer transaction to
3708  // make sure we don't hit metadata limits. The way we'll do that is:
3709  //
3710  // 1. Generate twice as many separate funded accounts as we have
3711  // offers.
3712  // 2.
3713  // a. One of these accounts mints an NFT with a full URL.
3714  // b. The other account makes an offer that will expire soon.
3715  // 3. After all of these offers have expired, cancel all of the
3716  // expired offers in a single transaction.
3717  //
3718  // I can't think of any way to increase the metadata beyond this,
3719  // but I'm open to ideas.
3720  Account const alice("alice");
3721  env.fund(XRP(1000), alice);
3722  env.close();
3723 
3724  std::string const uri(maxTokenURILength, '?');
3725  std::vector<uint256> offerIndexes;
3726  offerIndexes.reserve(maxTokenOfferCancelCount + 1);
3727  for (uint32_t i = 0; i < maxTokenOfferCancelCount + 1; ++i)
3728  {
3729  Account const nftAcct(std::string("nftAcct") + std::to_string(i));
3730  Account const offerAcct(
3731  std::string("offerAcct") + std::to_string(i));
3732  env.fund(XRP(1000), nftAcct, offerAcct);
3733  env.close();
3734 
3735  uint256 const nftokenID =
3736  token::getNextID(env, nftAcct, 0, tfTransferable);
3737  env(token::mint(nftAcct, 0),
3738  token::uri(uri),
3739  txflags(tfTransferable));
3740  env.close();
3741 
3742  offerIndexes.push_back(
3743  keylet::nftoffer(offerAcct, env.seq(offerAcct)).key);
3744  env(token::createOffer(offerAcct, nftokenID, drops(1)),
3745  token::owner(nftAcct),
3746  token::expiration(lastClose(env) + 5));
3747  env.close();
3748  }
3749 
3750  // Close the ledger so the last of the offers expire.
3751  env.close();
3752 
3753  // All offers should be in the ledger.
3754  for (uint256 const& offerIndex : offerIndexes)
3755  {
3756  BEAST_EXPECT(env.le(keylet::nftoffer(offerIndex)));
3757  }
3758 
3759  // alice attempts to cancel all of the expired offers. There is one
3760  // too many so the request fails.
3761  env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3762  env.close();
3763 
3764  // However alice can cancel just one of the offers.
3765  env(token::cancelOffer(alice, {offerIndexes.back()}));
3766  env.close();
3767 
3768  // Verify that offer is gone from the ledger.
3769  BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndexes.back())));
3770  offerIndexes.pop_back();
3771 
3772  // But alice adds a sell offer to the list...
3773  {
3774  uint256 const nftokenID =
3775  token::getNextID(env, alice, 0, tfTransferable);
3776  env(token::mint(alice, 0),
3777  token::uri(uri),
3778  txflags(tfTransferable));
3779  env.close();
3780 
3781  offerIndexes.push_back(keylet::nftoffer(alice, env.seq(alice)).key);
3782  env(token::createOffer(alice, nftokenID, drops(1)),
3783  txflags(tfSellNFToken));
3784  env.close();
3785 
3786  // alice's owner count should now to 2 for the nft and the offer.
3787  BEAST_EXPECT(ownerCount(env, alice) == 2);
3788 
3789  // Because alice added the sell offer there are still too many
3790  // offers in the list to cancel.
3791  env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3792  env.close();
3793 
3794  // alice burns her nft which removes the nft and the offer.
3795  env(token::burn(alice, nftokenID));
3796  env.close();
3797 
3798  // If alice's owner count is zero we can see that the offer
3799  // and nft are both gone.
3800  BEAST_EXPECT(ownerCount(env, alice) == 0);
3801  offerIndexes.pop_back();
3802  }
3803 
3804  // Now there are few enough offers in the list that they can all
3805  // be cancelled in a single transaction.
3806  env(token::cancelOffer(alice, offerIndexes));
3807  env.close();
3808 
3809  // Verify that remaining offers are gone from the ledger.
3810  for (uint256 const& offerIndex : offerIndexes)
3811  {
3812  BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndex)));
3813  }
3814  }
3815 
3816  void
3818  {
3819  // Look at the case where too many offers are passed in a cancel.
3820  testcase("Brokered NFT offer accept");
3821 
3822  using namespace test::jtx;
3823 
3824  Env env{*this, features};
3825 
3826  // The most important thing to explore here is the way funds are
3827  // assigned from the buyer to...
3828  // o the Seller,
3829  // o the Broker, and
3830  // o the Issuer (in the case of a transfer fee).
3831 
3832  Account const issuer{"issuer"};
3833  Account const minter{"minter"};
3834  Account const buyer{"buyer"};
3835  Account const broker{"broker"};
3836  Account const gw{"gw"};
3837  IOU const gwXAU(gw["XAU"]);
3838 
3839  env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3840  env.close();
3841 
3842  env(trust(issuer, gwXAU(2000)));
3843  env(trust(minter, gwXAU(2000)));
3844  env(trust(buyer, gwXAU(2000)));
3845  env(trust(broker, gwXAU(2000)));
3846  env.close();
3847 
3848  env(token::setMinter(issuer, minter));
3849  env.close();
3850 
3851  // Lambda to check owner count of all accounts is one.
3852  auto checkOwnerCountIsOne =
3853  [this, &env](
3855  accounts,
3856  int line) {
3857  for (Account const& acct : accounts)
3858  {
3859  if (std::uint32_t ownerCount = this->ownerCount(env, acct);
3860  ownerCount != 1)
3861  {
3862  std::stringstream ss;
3863  ss << "Account " << acct.human()
3864  << " expected ownerCount == 1. Got " << ownerCount;
3865  fail(ss.str(), __FILE__, line);
3866  }
3867  }
3868  };
3869 
3870  // Lambda that mints an NFT and returns the nftID.
3871  auto mintNFT = [&env, &issuer, &minter](std::uint16_t xferFee = 0) {
3872  uint256 const nftID =
3873  token::getNextID(env, issuer, 0, tfTransferable, xferFee);
3874  env(token::mint(minter, 0),
3875  token::issuer(issuer),
3876  token::xferFee(xferFee),
3877  txflags(tfTransferable));
3878  env.close();
3879  return nftID;
3880  };
3881 
3882  // o Seller is selling for zero XRP.
3883  // o Broker charges no fee.
3884  // o No transfer fee.
3885  //
3886  // Since minter is selling for zero the currency must be XRP.
3887  {
3888  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3889 
3890  uint256 const nftID = mintNFT();
3891 
3892  // minter creates their offer.
3893  uint256 const minterOfferIndex =
3894  keylet::nftoffer(minter, env.seq(minter)).key;
3895  env(token::createOffer(minter, nftID, XRP(0)),
3896  txflags(tfSellNFToken));
3897  env.close();
3898 
3899  // buyer creates their offer. Note: a buy offer can never
3900  // offer zero.
3901  uint256 const buyOfferIndex =
3902  keylet::nftoffer(buyer, env.seq(buyer)).key;
3903  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3904  env.close();
3905 
3906  auto const minterBalance = env.balance(minter);
3907  auto const buyerBalance = env.balance(buyer);
3908  auto const brokerBalance = env.balance(broker);
3909  auto const issuerBalance = env.balance(issuer);
3910 
3911  // Broker charges no brokerFee.
3912  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3913  env.close();
3914 
3915  // Note that minter's XRP balance goes up even though they
3916  // requested XRP(0).
3917  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3918  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3919  BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3920  BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3921 
3922  // Burn the NFT so the next test starts with a clean state.
3923  env(token::burn(buyer, nftID));
3924  env.close();
3925  }
3926 
3927  // o Seller is selling for zero XRP.
3928  // o Broker charges a fee.
3929  // o No transfer fee.
3930  //
3931  // Since minter is selling for zero the currency must be XRP.
3932  {
3933  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3934 
3935  uint256 const nftID = mintNFT();
3936 
3937  // minter creates their offer.
3938  uint256 const minterOfferIndex =
3939  keylet::nftoffer(minter, env.seq(minter)).key;
3940  env(token::createOffer(minter, nftID, XRP(0)),
3941  txflags(tfSellNFToken));
3942  env.close();
3943 
3944  // buyer creates their offer. Note: a buy offer can never
3945  // offer zero.
3946  uint256 const buyOfferIndex =
3947  keylet::nftoffer(buyer, env.seq(buyer)).key;
3948  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3949  env.close();
3950 
3951  // Broker attempts to charge a 1.1 XRP brokerFee and fails.
3952  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3953  token::brokerFee(XRP(1.1)),
3955  env.close();
3956 
3957  auto const minterBalance = env.balance(minter);
3958  auto const buyerBalance = env.balance(buyer);
3959  auto const brokerBalance = env.balance(broker);
3960  auto const issuerBalance = env.balance(issuer);
3961 
3962  // Broker charges a 0.5 XRP brokerFee.
3963  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3964  token::brokerFee(XRP(0.5)));
3965  env.close();
3966 
3967  // Note that minter's XRP balance goes up even though they
3968  // requested XRP(0).
3969  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3970  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3971  BEAST_EXPECT(
3972  env.balance(broker) == brokerBalance + XRP(0.5) - drops(10));
3973  BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3974 
3975  // Burn the NFT so the next test starts with a clean state.
3976  env(token::burn(buyer, nftID));
3977  env.close();
3978  }
3979 
3980  // o Seller is selling for zero XRP.
3981  // o Broker charges no fee.
3982  // o 50% transfer fee.
3983  //
3984  // Since minter is selling for zero the currency must be XRP.
3985  {
3986  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3987 
3988  uint256 const nftID = mintNFT(maxTransferFee);
3989 
3990  // minter creates their offer.
3991  uint256 const minterOfferIndex =
3992  keylet::nftoffer(minter, env.seq(minter)).key;
3993  env(token::createOffer(minter, nftID, XRP(0)),
3994  txflags(tfSellNFToken));
3995  env.close();
3996 
3997  // buyer creates their offer. Note: a buy offer can never
3998  // offer zero.
3999  uint256 const buyOfferIndex =
4000  keylet::nftoffer(buyer, env.seq(buyer)).key;
4001  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
4002  env.close();
4003 
4004  auto const minterBalance = env.balance(minter);
4005  auto const buyerBalance = env.balance(buyer);
4006  auto const brokerBalance = env.balance(broker);
4007  auto const issuerBalance = env.balance(issuer);
4008 
4009  // Broker charges no brokerFee.
4010  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
4011  env.close();
4012 
4013  // Note that minter's XRP balance goes up even though they
4014  // requested XRP(0).
4015  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4016  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4017  BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
4018  BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
4019 
4020  // Burn the NFT so the next test starts with a clean state.
4021  env(token::burn(buyer, nftID));
4022  env.close();
4023  }
4024 
4025  // o Seller is selling for zero XRP.
4026  // o Broker charges 0.5 XRP.
4027  // o 50% transfer fee.
4028  //
4029  // Since minter is selling for zero the currency must be XRP.
4030  {
4031  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4032 
4033  uint256 const nftID = mintNFT(maxTransferFee);
4034 
4035  // minter creates their offer.
4036  uint256 const minterOfferIndex =
4037  keylet::nftoffer(minter, env.seq(minter)).key;
4038  env(token::createOffer(minter, nftID, XRP(0)),
4039  txflags(tfSellNFToken));
4040  env.close();
4041 
4042  // buyer creates their offer. Note: a buy offer can never
4043  // offer zero.
4044  uint256 const buyOfferIndex =
4045  keylet::nftoffer(buyer, env.seq(buyer)).key;
4046  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
4047  env.close();
4048 
4049  auto const minterBalance = env.balance(minter);
4050  auto const buyerBalance = env.balance(buyer);
4051  auto const brokerBalance = env.balance(broker);
4052  auto const issuerBalance = env.balance(issuer);
4053 
4054  // Broker charges a 0.75 XRP brokerFee.
4055  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4056  token::brokerFee(XRP(0.75)));
4057  env.close();
4058 
4059  // Note that, with a 50% transfer fee, issuer gets 1/2 of what's
4060  // left _after_ broker takes their fee. minter gets the remainder
4061  // after both broker and minter take their cuts
4062  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
4063  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4064  BEAST_EXPECT(
4065  env.balance(broker) == brokerBalance + XRP(0.75) - drops(10));
4066  BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
4067 
4068  // Burn the NFT so the next test starts with a clean state.
4069  env(token::burn(buyer, nftID));
4070  env.close();
4071  }
4072 
4073  // Lambda to set the balance of all passed in accounts to gwXAU(1000).
4074  auto setXAUBalance_1000 =
4075  [this, &gw, &gwXAU, &env](
4077  accounts,
4078  int line) {
4079  for (Account const& acct : accounts)
4080  {
4081  static const auto xau1000 = gwXAU(1000);
4082  auto const balance = env.balance(acct, gwXAU);
4083  if (balance < xau1000)
4084  {
4085  env(pay(gw, acct, xau1000 - balance));
4086  env.close();
4087  }
4088  else if (balance > xau1000)
4089  {
4090  env(pay(acct, gw, balance - xau1000));
4091  env.close();
4092  }
4093  if (env.balance(acct, gwXAU) != xau1000)
4094  {
4095  std::stringstream ss;
4096  ss << "Unable to set " << acct.human()
4097  << " account balance to gwXAU(1000)";
4098  this->fail(ss.str(), __FILE__, line);
4099  }
4100  }
4101  };
4102 
4103  // The buyer and seller have identical amounts and there is no
4104  // transfer fee.
4105  {
4106  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4107  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4108 
4109  uint256 const nftID = mintNFT();
4110 
4111  // minter creates their offer.
4112  uint256 const minterOfferIndex =
4113  keylet::nftoffer(minter, env.seq(minter)).key;
4114  env(token::createOffer(minter, nftID, gwXAU(1000)),
4115  txflags(tfSellNFToken));
4116  env.close();
4117 
4118  {
4119  // buyer creates an offer for more XAU than they currently own.
4120  uint256 const buyOfferIndex =
4121  keylet::nftoffer(buyer, env.seq(buyer)).key;
4122  env(token::createOffer(buyer, nftID, gwXAU(1001)),
4123  token::owner(minter));
4124  env.close();
4125 
4126  // broker attempts to broker the offers but cannot.
4127  env(token::brokerOffers(
4128  broker, buyOfferIndex, minterOfferIndex),
4129  ter(tecINSUFFICIENT_FUNDS));
4130  env.close();
4131 
4132  // Cancel buyer's bad offer so the next test starts in a
4133  // clean state.
4134  env(token::cancelOffer(buyer, {buyOfferIndex}));
4135  env.close();
4136  }
4137  {
4138  // buyer creates an offer for less that what minter is asking.
4139  uint256 const buyOfferIndex =
4140  keylet::nftoffer(buyer, env.seq(buyer)).key;
4141  env(token::createOffer(buyer, nftID, gwXAU(999)),
4142  token::owner(minter));
4143  env.close();
4144 
4145  // broker attempts to broker the offers but cannot.
4146  env(token::brokerOffers(
4147  broker, buyOfferIndex, minterOfferIndex),
4149  env.close();
4150 
4151  // Cancel buyer's bad offer so the next test starts in a
4152  // clean state.
4153  env(token::cancelOffer(buyer, {buyOfferIndex}));
4154  env.close();
4155  }
4156 
4157  // buyer creates a large enough offer.
4158  uint256 const buyOfferIndex =
4159  keylet::nftoffer(buyer, env.seq(buyer)).key;
4160  env(token::createOffer(buyer, nftID, gwXAU(1000)),
4161  token::owner(minter));
4162  env.close();
4163 
4164  // Broker attempts to charge a brokerFee but cannot.
4165  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4166  token::brokerFee(gwXAU(0.1)),
4168  env.close();
4169 
4170  // broker charges no brokerFee and succeeds.
4171  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
4172  env.close();
4173 
4174  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4175  BEAST_EXPECT(ownerCount(env, minter) == 1);
4176  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4177  BEAST_EXPECT(ownerCount(env, broker) == 1);
4178  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4179  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4180  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4181  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4182 
4183  // Burn the NFT so the next test starts with a clean state.
4184  env(token::burn(buyer, nftID));
4185  env.close();
4186  }
4187 
4188  // seller offers more than buyer is asking.
4189  // There are both transfer and broker fees.
4190  {
4191  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4192  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4193 
4194  uint256 const nftID = mintNFT(maxTransferFee);
4195 
4196  // minter creates their offer.
4197  uint256 const minterOfferIndex =
4198  keylet::nftoffer(minter, env.seq(minter)).key;
4199  env(token::createOffer(minter, nftID, gwXAU(900)),
4200  txflags(tfSellNFToken));
4201  env.close();
4202  {
4203  // buyer creates an offer for more XAU than they currently own.
4204  uint256 const buyOfferIndex =
4205  keylet::nftoffer(buyer, env.seq(buyer)).key;
4206  env(token::createOffer(buyer, nftID, gwXAU(1001)),
4207  token::owner(minter));
4208  env.close();
4209 
4210  // broker attempts to broker the offers but cannot.
4211  env(token::brokerOffers(
4212  broker, buyOfferIndex, minterOfferIndex),
4213  ter(tecINSUFFICIENT_FUNDS));
4214  env.close();
4215 
4216  // Cancel buyer's bad offer so the next test starts in a
4217  // clean state.
4218  env(token::cancelOffer(buyer, {buyOfferIndex}));
4219  env.close();
4220  }
4221  {
4222  // buyer creates an offer for less that what minter is asking.
4223  uint256 const buyOfferIndex =
4224  keylet::nftoffer(buyer, env.seq(buyer)).key;
4225  env(token::createOffer(buyer, nftID, gwXAU(899)),
4226  token::owner(minter));
4227  env.close();
4228 
4229  // broker attempts to broker the offers but cannot.
4230  env(token::brokerOffers(
4231  broker, buyOfferIndex, minterOfferIndex),
4233  env.close();
4234 
4235  // Cancel buyer's bad offer so the next test starts in a
4236  // clean state.
4237  env(token::cancelOffer(buyer, {buyOfferIndex}));
4238  env.close();
4239  }
4240  // buyer creates a large enough offer.
4241  uint256 const buyOfferIndex =
4242  keylet::nftoffer(buyer, env.seq(buyer)).key;
4243  env(token::createOffer(buyer, nftID, gwXAU(1000)),
4244  token::owner(minter));
4245  env.close();
4246 
4247  // Broker attempts to charge a brokerFee larger than the
4248  // difference between the two offers but cannot.
4249  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4250  token::brokerFee(gwXAU(101)),
4252  env.close();
4253 
4254  // broker charges the full difference between the two offers and
4255  // succeeds.
4256  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4257  token::brokerFee(gwXAU(100)));
4258  env.close();
4259 
4260  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4261  BEAST_EXPECT(ownerCount(env, minter) == 1);
4262  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4263  BEAST_EXPECT(ownerCount(env, broker) == 1);
4264  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4265  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4266  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4267  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4268 
4269  // Burn the NFT so the next test starts with a clean state.
4270  env(token::burn(buyer, nftID));
4271  env.close();
4272  }
4273  // seller offers more than buyer is asking.
4274  // There are both transfer and broker fees, but broker takes less than
4275  // the maximum.
4276  {
4277  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4278  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4279 
4280  uint256 const nftID = mintNFT(maxTransferFee / 2); // 25%
4281 
4282  // minter creates their offer.
4283  uint256 const minterOfferIndex =
4284  keylet::nftoffer(minter, env.seq(minter)).key;
4285  env(token::createOffer(minter, nftID, gwXAU(900)),
4286  txflags(tfSellNFToken));
4287  env.close();
4288 
4289  // buyer creates a large enough offer.
4290  uint256 const buyOfferIndex =
4291  keylet::nftoffer(buyer, env.seq(buyer)).key;
4292  env(token::createOffer(buyer, nftID, gwXAU(1000)),
4293  token::owner(minter));
4294  env.close();
4295 
4296  // broker charges half difference between the two offers and
4297  // succeeds. 25% of the remaining difference goes to issuer.
4298  // The rest goes to minter.
4299  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4300  token::brokerFee(gwXAU(50)));
4301  env.close();
4302 
4303  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4304  BEAST_EXPECT(ownerCount(env, minter) == 1);
4305  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4306  BEAST_EXPECT(ownerCount(env, broker) == 1);
4307  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4308  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4309  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4310  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4311 
4312  // Burn the NFT so the next test starts with a clean state.
4313  env(token::burn(buyer, nftID));
4314  env.close();
4315  }
4316  }
4317 
4318  void
4320  {
4321  // Verify the Owner field of an offer behaves as expected.
4322  testcase("NFToken offer owner");
4323 
4324  using namespace test::jtx;
4325 
4326  Env env{*this, features};
4327 
4328  Account const issuer{"issuer"};
4329  Account const buyer1{"buyer1"};
4330  Account const buyer2{"buyer2"};
4331  env.fund(XRP(10000), issuer, buyer1, buyer2);
4332  env.close();
4333 
4334  // issuer creates an NFT.
4335  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4336  env(token::mint(issuer, 0u), txflags(tfTransferable));
4337  env.close();
4338 
4339  // Prove that issuer now owns nftId.
4340  BEAST_EXPECT(nftCount(env, issuer) == 1);
4341  BEAST_EXPECT(nftCount(env, buyer1) == 0);
4342  BEAST_EXPECT(nftCount(env, buyer2) == 0);
4343 
4344  // Both buyer1 and buyer2 create buy offers for nftId.
4345  uint256 const buyer1OfferIndex =
4346  keylet::nftoffer(buyer1, env.seq(buyer1)).key;
4347  env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4348  uint256 const buyer2OfferIndex =
4349  keylet::nftoffer(buyer2, env.seq(buyer2)).key;
4350  env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4351  env.close();
4352 
4353  // Lambda that counts the number of buy offers for a given NFT.
4354  auto nftBuyOfferCount = [&env](uint256 const& nftId) -> std::size_t {
4355  // We know that in this case not very many offers will be
4356  // returned, so we skip the marker stuff.
4357  Json::Value params;
4358  params[jss::nft_id] = to_string(nftId);
4359  Json::Value buyOffers =
4360  env.rpc("json", "nft_buy_offers", to_string(params));
4361 
4362  if (buyOffers.isMember(jss::result) &&
4363  buyOffers[jss::result].isMember(jss::offers))
4364  return buyOffers[jss::result][jss::offers].size();
4365 
4366  return 0;
4367  };
4368 
4369  // Show there are two buy offers for nftId.
4370  BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4371 
4372  // issuer accepts buyer1's offer.
4373  env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4374  env.close();
4375 
4376  // Prove that buyer1 now owns nftId.
4377  BEAST_EXPECT(nftCount(env, issuer) == 0);
4378  BEAST_EXPECT(nftCount(env, buyer1) == 1);
4379  BEAST_EXPECT(nftCount(env, buyer2) == 0);
4380 
4381  // buyer1's offer was consumed, but buyer2's offer is still in the
4382  // ledger.
4383  BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4384 
4385  // buyer1 can now accept buyer2's offer, even though buyer2's
4386  // NFTokenCreateOffer transaction specified the NFT Owner as issuer.
4387  env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4388  env.close();
4389 
4390  // Prove that buyer2 now owns nftId.
4391  BEAST_EXPECT(nftCount(env, issuer) == 0);
4392  BEAST_EXPECT(nftCount(env, buyer1) == 0);
4393  BEAST_EXPECT(nftCount(env, buyer2) == 1);
4394 
4395  // All of the NFTokenOffers are now consumed.
4396  BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4397  }
4398 
4399  void
4401  {
4402  // Make sure all NFToken transactions work with tickets.
4403  testcase("NFToken transactions with tickets");
4404 
4405  using namespace test::jtx;
4406 
4407  Env env{*this, features};
4408 
4409  Account const issuer{"issuer"};
4410  Account const buyer{"buyer"};
4411  env.fund(XRP(10000), issuer, buyer);
4412  env.close();
4413 
4414  // issuer and buyer grab enough tickets for all of the following
4415  // transactions. Note that once the tickets are acquired issuer's
4416  // and buyer's account sequence numbers should not advance.
4417  std::uint32_t issuerTicketSeq{env.seq(issuer) + 1};
4418  env(ticket::create(issuer, 10));
4419  env.close();
4420  std::uint32_t const issuerSeq{env.seq(issuer)};
4421  BEAST_EXPECT(ticketCount(env, issuer) == 10);
4422 
4423  std::uint32_t buyerTicketSeq{env.seq(buyer) + 1};
4424  env(ticket::create(buyer, 10));
4425  env.close();
4426  std::uint32_t const buyerSeq{env.seq(buyer)};
4427  BEAST_EXPECT(ticketCount(env, buyer) == 10);
4428 
4429  // NFTokenMint
4430  BEAST_EXPECT(ownerCount(env, issuer) == 10);
4431  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4432  env(token::mint(issuer, 0u),
4433  txflags(tfTransferable),
4434  ticket::use(issuerTicketSeq++));
4435  env.close();
4436  BEAST_EXPECT(ownerCount(env, issuer) == 10);
4437  BEAST_EXPECT(ticketCount(env, issuer) == 9);
4438 
4439  // NFTokenCreateOffer
4440  BEAST_EXPECT(ownerCount(env, buyer) == 10);
4441  uint256 const offerIndex0 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4442  env(token::createOffer(buyer, nftId, XRP(1)),
4443  token::owner(issuer),
4444  ticket::use(buyerTicketSeq++));
4445  env.close();
4446  BEAST_EXPECT(ownerCount(env, buyer) == 10);
4447  BEAST_EXPECT(ticketCount(env, buyer) == 9);
4448 
4449  // NFTokenCancelOffer
4450  env(token::cancelOffer(buyer, {offerIndex0}),
4451  ticket::use(buyerTicketSeq++));
4452  env.close();
4453  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4454  BEAST_EXPECT(ticketCount(env, buyer) == 8);
4455 
4456  // NFTokenCreateOffer. buyer tries again.
4457  uint256 const offerIndex1 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4458  env(token::createOffer(buyer, nftId, XRP(2)),
4459  token::owner(issuer),
4460  ticket::use(buyerTicketSeq++));
4461  env.close();
4462  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4463  BEAST_EXPECT(ticketCount(env, buyer) == 7);
4464 
4465  // NFTokenAcceptOffer. issuer accepts buyer's offer.
4466  env(token::acceptBuyOffer(issuer, offerIndex1),
4467  ticket::use(issuerTicketSeq++));
4468  env.close();
4469  BEAST_EXPECT(ownerCount(env, issuer) == 8);
4470  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4471  BEAST_EXPECT(ticketCount(env, issuer) == 8);
4472 
4473  // NFTokenBurn. buyer burns the token they just bought.
4474  env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4475  env.close();
4476  BEAST_EXPECT(ownerCount(env, issuer) == 8);
4477  BEAST_EXPECT(ownerCount(env, buyer) == 6);
4478  BEAST_EXPECT(ticketCount(env, buyer) == 6);
4479 
4480  // Verify that the account sequence numbers did not advance.
4481  BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4482  BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4483  }
4484 
4485  void
4487  {
4488  // Account deletion rules with NFTs:
4489  // 1. An account holding one or more NFT offers may be deleted.
4490  // 2. An NFT issuer with any NFTs they have issued still in the
4491  // ledger may not be deleted.
4492  // 3. An account holding one or more NFTs may not be deleted.
4493  testcase("NFToken delete account");
4494 
4495  using namespace test::jtx;
4496 
4497  Env env{*this, features};
4498 
4499  Account const issuer{"issuer"};
4500  Account const minter{"minter"};
4501  Account const becky{"becky"};
4502  Account const carla{"carla"};
4503  Account const daria{"daria"};
4504 
4505  env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4506  env.close();
4507 
4508  // Allow enough ledgers to pass so any of these accounts can be deleted.
4509  for (int i = 0; i < 300; ++i)
4510  env.close();
4511 
4512  env(token::setMinter(issuer, minter));
4513  env.close();
4514 
4515  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4516  env(token::mint(minter, 0u),
4517  token::issuer(issuer),
4518  txflags(tfTransferable));
4519  env.close();
4520 
4521  // At the momement issuer and minter cannot delete themselves.
4522  // o issuer has an issued NFT in the ledger.
4523  // o minter owns an NFT.
4524  env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4525  env(acctdelete(minter, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4526  env.close();
4527 
4528  // Let enough ledgers pass so the account delete transactions are
4529  // not retried.
4530  for (int i = 0; i < 15; ++i)
4531  env.close();
4532 
4533  // becky and carla create offers for minter's NFT.
4534  env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4535  env.close();
4536 
4537  uint256 const carlaOfferIndex =
4538  keylet::nftoffer(carla, env.seq(carla)).key;
4539  env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4540  env.close();
4541 
4542  // It should be possible for becky to delete herself, even though
4543  // becky has an active NFT offer.
4544  env(acctdelete(becky, daria), fee(XRP(50)));
4545  env.close();
4546 
4547  // minter accepts carla's offer.
4548  env(token::acceptBuyOffer(minter, carlaOfferIndex));
4549  env.close();
4550 
4551  // Now it should be possible for minter to delete themselves since
4552  // they no longer own an NFT.
4553  env(acctdelete(minter, daria), fee(XRP(50)));
4554  env.close();
4555 
4556  // 1. issuer cannot delete themselves because they issued an NFT that
4557  // is still in the ledger.
4558  // 2. carla owns an NFT, so she cannot delete herself.
4559  env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4560  env(acctdelete(carla, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4561  env.close();
4562 
4563  // Let enough ledgers pass so the account delete transactions are
4564  // not retried.
4565  for (int i = 0; i < 15; ++i)
4566  env.close();
4567 
4568  // carla burns her NFT. Since issuer's NFT is no longer in the
4569  // ledger, both issuer and carla can delete themselves.
4570  env(token::burn(carla, nftId));
4571  env.close();
4572 
4573  env(acctdelete(issuer, daria), fee(XRP(50)));
4574  env(acctdelete(carla, daria), fee(XRP(50)));
4575  env.close();
4576  }
4577 
4578  void
4580  {
4581  testcase("nft_buy_offers and nft_sell_offers");
4582 
4583  // The default limit on returned NFToken offers is 250, so we need
4584  // to produce more than 250 offers of each kind in order to exercise
4585  // the marker.
4586 
4587  // Fortunately there's nothing in the rules that says an account
4588  // can't hold more than one offer for the same NFT. So we only
4589  // need two accounts to generate the necessary offers.
4590  using namespace test::jtx;
4591 
4592  Env env{*this, features};
4593 
4594  Account const issuer{"issuer"};
4595  Account const buyer{"buyer"};
4596 
4597  // A lot of offers requires a lot for reserve.
4598  env.fund(XRP(1000000), issuer, buyer);
4599  env.close();
4600 
4601  // Create an NFT that we'll make offers for.
4602  uint256 const nftID{token::getNextID(env, issuer, 0u, tfTransferable)};
4603  env(token::mint(issuer, 0), txflags(tfTransferable));
4604  env.close();
4605 
4606  // A lambda that validates nft_XXX_offers query responses.
4607  auto checkOffers = [this, &env, &nftID](
4608  char const* request,
4609  int expectCount,
4610  int expectMarkerCount,
4611  int line) {
4612  int markerCount = 0;
4613  Json::Value allOffers(Json::arrayValue);
4614  std::string marker;
4615 
4616  // The do/while collects results until no marker is returned.
4617  do
4618  {
4619  Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4620  Json::Value params;
4621  params[jss::nft_id] = to_string(nftID);
4622 
4623  if (!marker.empty())
4624  params[jss::marker] = marker;
4625  return env.rpc("json", request, to_string(params));
4626  }();
4627 
4628  // If there are no offers for the NFT we get an error
4629  if (expectCount == 0)
4630  {
4631  if (expect(
4632  nftOffers.isMember(jss::result),
4633  "expected \"result\"",
4634  __FILE__,
4635  line))
4636  {
4637  if (expect(
4638  nftOffers[jss::result].isMember(jss::error),
4639  "expected \"error\"",
4640  __FILE__,
4641  line))
4642  {
4643  expect(
4644  nftOffers[jss::result][jss::error].asString() ==
4645  "objectNotFound",
4646  "expected \"objectNotFound\"",
4647  __FILE__,
4648  line);
4649  }
4650  }
4651  break;
4652  }
4653 
4654  marker.clear();
4655  if (expect(
4656  nftOffers.isMember(jss::result),
4657  "expected \"result\"",
4658  __FILE__,
4659  line))
4660  {
4661  Json::Value& result = nftOffers[jss::result];
4662 
4663  if (result.isMember(jss::marker))
4664  {
4665  ++markerCount;
4666  marker = result[jss::marker].asString();
4667  }
4668 
4669  if (expect(
4670  result.isMember(jss::offers),
4671  "expected \"offers\"",
4672  __FILE__,
4673  line))
4674  {
4675  Json::Value& someOffers = result[jss::offers];
4676  for (std::size_t i = 0; i < someOffers.size(); ++i)
4677  allOffers.append(someOffers[i]);
4678  }
4679  }
4680  } while (!marker.empty());
4681 
4682  // Verify the contents of allOffers makes sense.
4683  expect(
4684  allOffers.size() == expectCount,
4685  "Unexpected returned offer count",
4686  __FILE__,
4687  line);
4688  expect(
4689  markerCount == expectMarkerCount,
4690  "Unexpected marker count",
4691  __FILE__,
4692  line);
4693  std::optional<int> globalFlags;
4694  std::set<std::string> offerIndexes;
4695  std::set<std::string> amounts;
4696  for (Json::Value const& offer : allOffers)
4697  {
4698  // The flags on all found offers should be the same.
4699  if (!globalFlags)
4700  globalFlags = offer[jss::flags].asInt();
4701 
4702  expect(
4703  *globalFlags == offer[jss::flags].asInt(),
4704  "Inconsistent flags returned",
4705  __FILE__,
4706  line);
4707 
4708  // The test conditions should produce unique indexes and
4709  // amounts for all offers.
4710  offerIndexes.insert(offer[jss::nft_offer_index].asString());
4711  amounts.insert(offer[jss::amount].asString());
4712  }
4713 
4714  expect(
4715  offerIndexes.size() == expectCount,
4716  "Duplicate indexes returned?",
4717  __FILE__,
4718  line);
4719  expect(
4720  amounts.size() == expectCount,
4721  "Duplicate amounts returned?",
4722  __FILE__,
4723  line);
4724  };
4725 
4726  // There are no sell offers.
4727  checkOffers("nft_sell_offers", 0, false, __LINE__);
4728 
4729  // A lambda that generates sell offers.
4730  STAmount sellPrice = XRP(0);
4731  auto makeSellOffers =
4732  [&env, &issuer, &nftID, &sellPrice](STAmount const& limit) {
4733  // Save a little test time by not closing too often.
4734  int offerCount = 0;
4735  while (sellPrice < limit)
4736  {
4737  sellPrice += XRP(1);
4738  env(token::createOffer(issuer, nftID, sellPrice),
4739  txflags(tfSellNFToken));
4740  if (++offerCount % 10 == 0)
4741  env.close();
4742  }
4743  env.close();
4744  };
4745 
4746  // There is one sell offer.
4747  makeSellOffers(XRP(1));
4748  checkOffers("nft_sell_offers", 1, 0, __LINE__);
4749 
4750  // There are 250 sell offers.
4751  makeSellOffers(XRP(250));
4752  checkOffers("nft_sell_offers", 250, 0, __LINE__);
4753 
4754  // There are 251 sell offers.
4755  makeSellOffers(XRP(251));
4756  checkOffers("nft_sell_offers", 251, 1, __LINE__);
4757 
4758  // There are 500 sell offers.
4759  makeSellOffers(XRP(500));
4760  checkOffers("nft_sell_offers", 500, 1, __LINE__);
4761 
4762  // There are 501 sell offers.
4763  makeSellOffers(XRP(501));
4764  checkOffers("nft_sell_offers", 501, 2, __LINE__);
4765 
4766  // There are no buy offers.
4767  checkOffers("nft_buy_offers", 0, 0, __LINE__);
4768 
4769  // A lambda that generates buy offers.
4770  STAmount buyPrice = XRP(0);
4771  auto makeBuyOffers =
4772  [&env, &buyer, &issuer, &nftID, &buyPrice](STAmount const& limit) {
4773  // Save a little test time by not closing too often.
4774  int offerCount = 0;
4775  while (buyPrice < limit)
4776  {
4777  buyPrice += XRP(1);
4778  env(token::createOffer(buyer, nftID, buyPrice),
4779  token::owner(issuer));
4780  if (++offerCount % 10 == 0)
4781  env.close();
4782  }
4783  env.close();
4784  };
4785 
4786  // There is one buy offer;
4787  makeBuyOffers(XRP(1));
4788  checkOffers("nft_buy_offers", 1, 0, __LINE__);
4789 
4790  // There are 250 buy offers.
4791  makeBuyOffers(XRP(250));
4792  checkOffers("nft_buy_offers", 250, 0, __LINE__);
4793 
4794  // There are 251 buy offers.
4795  makeBuyOffers(XRP(251));
4796  checkOffers("nft_buy_offers", 251, 1, __LINE__);
4797 
4798  // There are 500 buy offers.
4799  makeBuyOffers(XRP(500));
4800  checkOffers("nft_buy_offers", 500, 1, __LINE__);
4801 
4802  // There are 501 buy offers.
4803  makeBuyOffers(XRP(501));
4804  checkOffers("nft_buy_offers", 501, 2, __LINE__);
4805  }
4806 
4807  void
4809  {
4810  // Exercise changes introduced by fixNFTokenNegOffer.
4811  using namespace test::jtx;
4812 
4813  testcase("fixNFTokenNegOffer");
4814 
4815  Account const issuer{"issuer"};
4816  Account const buyer{"buyer"};
4817  Account const gw{"gw"};
4818  IOU const gwXAU(gw["XAU"]);
4819 
4820  // Test both with and without fixNFTokenNegOffer
4821  for (auto const& tweakedFeatures :
4823  features | fixNFTokenNegOffer})
4824  {
4825  // There was a bug in the initial NFT implementation that
4826  // allowed offers to be placed with negative amounts. Verify
4827  // that fixNFTokenNegOffer addresses the problem.
4828  Env env{*this, tweakedFeatures};
4829 
4830  env.fund(XRP(1000000), issuer, buyer, gw);
4831  env.close();
4832 
4833  env(trust(issuer, gwXAU(2000)));
4834  env(trust(buyer, gwXAU(2000)));
4835  env.close();
4836 
4837  env(pay(gw, issuer, gwXAU(1000)));
4838  env(pay(gw, buyer, gwXAU(1000)));
4839  env.close();
4840 
4841  // Create an NFT that we'll make XRP offers for.
4842  uint256 const nftID0{
4843  token::getNextID(env, issuer, 0u, tfTransferable)};
4844  env(token::mint(issuer, 0), txflags(tfTransferable));
4845  env.close();
4846 
4847  // Create an NFT that we'll make IOU offers for.
4848  uint256 const nftID1{
4849  token::getNextID(env, issuer, 1u, tfTransferable)};
4850  env(token::mint(issuer, 1), txflags(tfTransferable));
4851  env.close();
4852 
4853  TER const offerCreateTER = tweakedFeatures[fixNFTokenNegOffer]
4854  ? static_cast<TER>(temBAD_AMOUNT)
4855  : static_cast<TER>(tesSUCCESS);
4856 
4857  // Make offers with negative amounts for the NFTs
4858  uint256 const sellNegXrpOfferIndex =
4859  keylet::nftoffer(issuer, env.seq(issuer)).key;
4860  env(token::createOffer(issuer, nftID0, XRP(-2)),
4861  txflags(tfSellNFToken),
4862  ter(offerCreateTER));
4863  env.close();
4864 
4865  uint256 const sellNegIouOfferIndex =
4866  keylet::nftoffer(issuer, env.seq(issuer)).key;
4867  env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4868  txflags(tfSellNFToken),
4869  ter(offerCreateTER));
4870  env.close();
4871 
4872  uint256 const buyNegXrpOfferIndex =
4873  keylet::nftoffer(buyer, env.seq(buyer)).key;
4874  env(token::createOffer(buyer, nftID0, XRP(-1)),
4875  token::owner(issuer),
4876  ter(offerCreateTER));
4877  env.close();
4878 
4879  uint256 const buyNegIouOfferIndex =
4880  keylet::nftoffer(buyer, env.seq(buyer)).key;
4881  env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4882  token::owner(issuer),
4883  ter(offerCreateTER));
4884  env.close();
4885 
4886  {
4887  // Now try to accept the offers.
4888  // 1. If fixNFTokenNegOffer is NOT enabled get tecINTERNAL.
4889  // 2. If fixNFTokenNegOffer IS enabled get tecOBJECT_NOT_FOUND.
4890  TER const offerAcceptTER = tweakedFeatures[fixNFTokenNegOffer]
4891  ? static_cast<TER>(tecOBJECT_NOT_FOUND)
4892  : static_cast<TER>(tecINTERNAL);
4893 
4894  // Sell offers.
4895  env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4896  ter(offerAcceptTER));
4897  env.close();
4898  env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4899  ter(offerAcceptTER));
4900  env.close();
4901 
4902  // Buy offers.
4903  env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
4904  ter(offerAcceptTER));
4905  env.close();
4906  env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
4907  ter(offerAcceptTER));
4908  env.close();
4909  }
4910  {
4911  // 1. If fixNFTokenNegOffer is NOT enabled get tecSUCCESS.
4912  // 2. If fixNFTokenNegOffer IS enabled get tecOBJECT_NOT_FOUND.
4913  TER const offerAcceptTER = tweakedFeatures[fixNFTokenNegOffer]
4914  ? static_cast<TER>(tecOBJECT_NOT_FOUND)
4915  : static_cast<TER>(tesSUCCESS);
4916 
4917  // Brokered offers.
4918  env(token::brokerOffers(
4919  gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
4920  ter(offerAcceptTER));
4921  env.close();
4922  env(token::brokerOffers(
4923  gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
4924  ter(offerAcceptTER));
4925  env.close();
4926  }
4927  }
4928 
4929  // Test what happens if NFTokenOffers are created with negative amounts
4930  // and then fixNFTokenNegOffer goes live. What does an acceptOffer do?
4931  {
4932  Env env{
4933  *this,
4935 
4936  env.fund(XRP(1000000), issuer, buyer, gw);
4937  env.close();
4938 
4939  env(trust(issuer, gwXAU(2000)));
4940  env(trust(buyer, gwXAU(2000)));
4941  env.close();
4942 
4943  env(pay(gw, issuer, gwXAU(1000)));
4944  env(pay(gw, buyer, gwXAU(1000)));
4945  env.close();
4946 
4947  // Create an NFT that we'll make XRP offers for.
4948  uint256 const nftID0{
4949  token::getNextID(env, issuer, 0u, tfTransferable)};
4950  env(token::mint(issuer, 0), txflags(tfTransferable));
4951  env.close();
4952 
4953  // Create an NFT that we'll make IOU offers for.
4954  uint256 const nftID1{
4955  token::getNextID(env, issuer, 1u, tfTransferable)};
4956  env(token::mint(issuer, 1), txflags(tfTransferable));
4957  env.close();
4958 
4959  // Make offers with negative amounts for the NFTs
4960  uint256 const sellNegXrpOfferIndex =
4961  keylet::nftoffer(issuer, env.seq(issuer)).key;
4962  env(token::createOffer(issuer, nftID0, XRP(-2)),
4963  txflags(tfSellNFToken));
4964  env.close();
4965 
4966  uint256 const sellNegIouOfferIndex =
4967  keylet::nftoffer(issuer, env.seq(issuer)).key;
4968  env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4969  txflags(tfSellNFToken));
4970  env.close();
4971 
4972  uint256 const buyNegXrpOfferIndex =
4973  keylet::nftoffer(buyer, env.seq(buyer)).key;
4974  env(token::createOffer(buyer, nftID0, XRP(-1)),
4975  token::owner(issuer));
4976  env.close();
4977 
4978  uint256 const buyNegIouOfferIndex =
4979  keylet::nftoffer(buyer, env.seq(buyer)).key;
4980  env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4981  token::owner(issuer));
4982  env.close();
4983 
4984  // Now the amendment passes.
4985  env.enableFeature(fixNFTokenNegOffer);
4986  env.close();
4987 
4988  // All attempts to accept the offers with negative amounts
4989  // should fail with temBAD_OFFER.
4990  env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4991  ter(temBAD_OFFER));
4992  env.close();
4993  env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4994  ter(temBAD_OFFER));
4995  env.close();
4996 
4997  // Buy offers.
4998  env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
4999  ter(temBAD_OFFER));
5000  env.close();
5001  env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5002  ter(temBAD_OFFER));
5003  env.close();
5004 
5005  // Brokered offers.
5006  env(token::brokerOffers(
5007  gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5008  ter(temBAD_OFFER));
5009  env.close();
5010  env(token::brokerOffers(
5011  gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5012  ter(temBAD_OFFER));
5013  env.close();
5014  }
5015 
5016  // Test buy offers with a destination with and without
5017  // fixNFTokenNegOffer.
5018  for (auto const& tweakedFeatures :
5020  features | fixNFTokenNegOffer})
5021  {
5022  Env env{*this, tweakedFeatures};
5023 
5024  env.fund(XRP(1000000), issuer, buyer);
5025 
5026  // Create an NFT that we'll make offers for.
5027  uint256 const nftID{
5028  token::getNextID(env, issuer, 0u, tfTransferable)};
5029  env(token::mint(issuer, 0), txflags(tfTransferable));
5030  env.close();
5031 
5032  TER const offerCreateTER = tweakedFeatures[fixNFTokenNegOffer]
5033  ? static_cast<TER>(tesSUCCESS)
5034  : static_cast<TER>(temMALFORMED);
5035 
5036  env(token::createOffer(buyer, nftID, drops(1)),
5037  token::owner(issuer),
5038  token::destination(issuer),
5039  ter(offerCreateTER));
5040  env.close();
5041  }
5042  }
5043 
5044  void
5046  {
5047  testEnabled(features);
5048  testMintReserve(features);
5049  testMintMaxTokens(features);
5050  testMintInvalid(features);
5051  testBurnInvalid(features);
5052  testCreateOfferInvalid(features);
5053  testCancelOfferInvalid(features);
5054  testAcceptOfferInvalid(features);
5055  testMintFlagBurnable(features);
5056  testMintFlagOnlyXRP(features);
5057  testMintFlagCreateTrustLine(features);
5058  testMintFlagTransferable(features);
5059  testMintTransferFee(features);
5060  testMintTaxon(features);
5061  testMintURI(features);
5062  testCreateOfferDestination(features);
5064  testCreateOfferExpiration(features);
5065  testCancelOffers(features);
5066  testCancelTooManyOffers(features);
5067  testBrokeredAccept(features);
5068  testNFTokenOfferOwner(features);
5069  testNFTokenWithTickets(features);
5070  testNFTokenDeleteAccount(features);
5071  testNftXxxOffers(features);
5072  testFixNFTokenNegOffer(features);
5073  }
5074 
5075 public:
5076  void
5077  run() override
5078  {
5079  using namespace test::jtx;
5080  FeatureBitset const all{supported_amendments()};
5081  FeatureBitset const fixNFTDir{fixNFTokenDirV1};
5082 
5083  testWithFeats(all - fixNFTDir);
5085  testWithFeats(all);
5086  }
5087 };
5088 
5089 BEAST_DEFINE_TESTSUITE_PRIO(NFToken, tx, ripple, 2);
5090 
5091 } // namespace ripple
ripple::NFToken_test::testNFTokenDeleteAccount
void testNFTokenDeleteAccount(FeatureBitset features)
Definition: NFToken_test.cpp:4486
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:65
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:3110
ripple::NFToken_test::testNFTokenOfferOwner
void testNFTokenOfferOwner(FeatureBitset features)
Definition: NFToken_test.cpp:4319
ripple::sfNFTokenOffers
const SF_VECTOR256 sfNFTokenOffers
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::tecINSUFFICIENT_FUNDS
@ tecINSUFFICIENT_FUNDS
Definition: TER.h:289
std::string::reserve
T reserve(T... args)
ripple::TxSearched::all
@ all
ripple::OpenView
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:55
std::vector
STL class.
std::set::size
T size(T... args)
ripple::STAmount::getJson
Json::Value getJson(JsonOptions) const override
Definition: STAmount.cpp:614
ripple::sfMintedNFTokens
const SF_UINT32 sfMintedNFTokens
ripple::keylet::nftoffer
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition: Indexes.cpp:355
ripple::test::jtx::Account::human
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
std::stringstream
STL class.
ripple::NFToken_test::testCancelTooManyOffers
void testCancelTooManyOffers(FeatureBitset features)
Definition: NFToken_test.cpp:3698
ripple::STAmount::cMinOffset
static const int cMinOffset
Definition: STAmount.h:61
ripple::NFToken_test::ownerCount
static std::uint32_t ownerCount(test::jtx::Env const &env, test::jtx::Account const &acct)
Definition: NFToken_test.cpp: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:3584
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:5045
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:4808
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:5077
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:4579
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:2497
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:44
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:2981
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:3817
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::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:4400
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:2570
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:219
ripple::NFToken_test::testCreateOfferDestination
void testCreateOfferDestination(FeatureBitset features)
Definition: NFToken_test.cpp:2680
ripple::temBAD_NFTOKEN_TRANSFER_FEE
@ temBAD_NFTOKEN_TRANSFER_FEE
Definition: TER.h:122
std::set< std::string >
ripple::sfNFTokenSellOffer
const SF_UINT256 sfNFTokenSellOffer
ripple::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:300
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::tecNO_DST
@ tecNO_DST
Definition: TER.h:254
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:684
ripple::sfNFTokenBrokerFee
const SF_AMOUNT sfNFTokenBrokerFee
Json::Value
Represents a JSON value.
Definition: json_value.h:145
initializer_list
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469