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