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 present in the ledger.
1134  uint256 const missingOfferIndex = keylet::nftoffer(alice, 1).key;
1135  env(token::acceptBuyOffer(buyer, missingOfferIndex),
1136  ter(tecOBJECT_NOT_FOUND));
1137  env.close();
1138  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1139 
1140  // The buy offer must not have expired.
1141  env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
1142  env.close();
1143  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1144 
1145  // The sell offer must be present in the ledger.
1146  env(token::acceptSellOffer(buyer, missingOfferIndex),
1147  ter(tecOBJECT_NOT_FOUND));
1148  env.close();
1149  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1150 
1151  // The sell offer must not have expired.
1152  env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
1153  env.close();
1154  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1155 
1156  //----------------------------------------------------------------------
1157  // preclaim brokered
1158 
1159  // alice and buyer need trustlines before buyer can to create an
1160  // offer for gwAUD.
1161  env(trust(alice, gwAUD(1000)));
1162  env(trust(buyer, gwAUD(1000)));
1163  env.close();
1164  env(pay(gw, buyer, gwAUD(30)));
1165  env.close();
1166  BEAST_EXPECT(ownerCount(env, alice) == 7);
1167  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1168 
1169  // We're about to exercise offer brokering, so we need
1170  // corresponding buy and sell offers.
1171  {
1172  // buyer creates a buy offer for one of alice's nfts.
1173  uint256 const buyerOfferIndex =
1174  keylet::nftoffer(buyer, env.seq(buyer)).key;
1175  env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1176  token::owner(alice));
1177  env.close();
1178  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1179 
1180  // gw attempts to broker offers that are not for the same token.
1181  env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1183  env.close();
1184  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1185 
1186  // gw attempts to broker offers that are not for the same currency.
1187  env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1189  env.close();
1190  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1191 
1192  // In a brokered offer, the buyer must offer greater than or
1193  // equal to the selling price.
1194  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1196  env.close();
1197  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1198 
1199  // Remove buyer's offer.
1200  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1201  env.close();
1202  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1203  }
1204  {
1205  // buyer creates a buy offer for one of alice's nfts.
1206  uint256 const buyerOfferIndex =
1207  keylet::nftoffer(buyer, env.seq(buyer)).key;
1208  env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1209  token::owner(alice));
1210  env.close();
1211  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1212 
1213  // Broker sets their fee in a denomination other than the one
1214  // used by the offers
1215  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1216  token::brokerFee(XRP(40)),
1218  env.close();
1219  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1220 
1221  // Broker fee way too big.
1222  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1223  token::brokerFee(gwAUD(31)),
1225  env.close();
1226  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1227 
1228  // Broker fee is smaller, but still too big once the offer
1229  // seller's minimum is taken into account.
1230  env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1231  token::brokerFee(gwAUD(1.5)),
1233  env.close();
1234  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1235 
1236  // Remove buyer's offer.
1237  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1238  env.close();
1239  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1240  }
1241  //----------------------------------------------------------------------
1242  // preclaim buy
1243  {
1244  // buyer creates a buy offer for one of alice's nfts.
1245  uint256 const buyerOfferIndex =
1246  keylet::nftoffer(buyer, env.seq(buyer)).key;
1247  env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1248  token::owner(alice));
1249  env.close();
1250  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1251 
1252  // Don't accept a buy offer if the sell flag is set.
1253  env(token::acceptBuyOffer(buyer, plainOfferIndex),
1255  env.close();
1256  BEAST_EXPECT(ownerCount(env, alice) == 7);
1257 
1258  // An account can't accept its own offer.
1259  env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1261  env.close();
1262  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1263 
1264  // An offer acceptor must have enough funds to pay for the offer.
1265  env(pay(buyer, gw, gwAUD(30)));
1266  env.close();
1267  BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1268  env(token::acceptBuyOffer(alice, buyerOfferIndex),
1269  ter(tecINSUFFICIENT_FUNDS));
1270  env.close();
1271  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1272 
1273  // alice gives her NFT to gw, so alice no longer owns nftAlice0.
1274  {
1275  uint256 const offerIndex =
1276  keylet::nftoffer(alice, env.seq(alice)).key;
1277  env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1278  txflags(tfSellNFToken));
1279  env.close();
1280  env(token::acceptSellOffer(gw, offerIndex));
1281  env.close();
1282  BEAST_EXPECT(ownerCount(env, alice) == 7);
1283  }
1284  env(pay(gw, buyer, gwAUD(30)));
1285  env.close();
1286 
1287  // alice can't accept a buy offer for an NFT she no longer owns.
1288  env(token::acceptBuyOffer(alice, buyerOfferIndex),
1289  ter(tecNO_PERMISSION));
1290  env.close();
1291  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1292 
1293  // Remove buyer's offer.
1294  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1295  env.close();
1296  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1297  }
1298  //----------------------------------------------------------------------
1299  // preclaim sell
1300  {
1301  // buyer creates a buy offer for one of alice's nfts.
1302  uint256 const buyerOfferIndex =
1303  keylet::nftoffer(buyer, env.seq(buyer)).key;
1304  env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1305  token::owner(alice));
1306  env.close();
1307  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1308 
1309  // Don't accept a sell offer without the sell flag set.
1310  env(token::acceptSellOffer(alice, buyerOfferIndex),
1312  env.close();
1313  BEAST_EXPECT(ownerCount(env, alice) == 7);
1314 
1315  // An account can't accept its own offer.
1316  env(token::acceptSellOffer(alice, plainOfferIndex),
1318  env.close();
1319  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1320 
1321  // The seller must currently be in possession of the token they
1322  // are selling. alice gave nftAlice0ID to gw.
1323  env(token::acceptSellOffer(buyer, plainOfferIndex),
1324  ter(tecNO_PERMISSION));
1325  env.close();
1326  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1327 
1328  // gw gives nftAlice0ID back to alice. That allows us to check
1329  // buyer attempting to accept one of alice's offers with
1330  // insufficient funds.
1331  {
1332  uint256 const offerIndex =
1333  keylet::nftoffer(gw, env.seq(gw)).key;
1334  env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1335  txflags(tfSellNFToken));
1336  env.close();
1337  env(token::acceptSellOffer(alice, offerIndex));
1338  env.close();
1339  BEAST_EXPECT(ownerCount(env, alice) == 7);
1340  }
1341  env(pay(buyer, gw, gwAUD(30)));
1342  env.close();
1343  BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1344  env(token::acceptSellOffer(buyer, audOfferIndex),
1345  ter(tecINSUFFICIENT_FUNDS));
1346  env.close();
1347  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1348  }
1349 
1350  //----------------------------------------------------------------------
1351  // doApply
1352  //
1353  // As far as I can see none of the failure modes are accessible as
1354  // long as the preflight and preclaim conditions are met.
1355  }
1356 
1357  void
1359  {
1360  // Exercise NFTs with flagBurnable set and not set.
1361  testcase("Mint flagBurnable");
1362 
1363  using namespace test::jtx;
1364 
1365  Env env{*this, features};
1366  Account const alice{"alice"};
1367  Account const buyer{"buyer"};
1368  Account const minter1{"minter1"};
1369  Account const minter2{"minter2"};
1370 
1371  env.fund(XRP(1000), alice, buyer, minter1, minter2);
1372  env.close();
1373  BEAST_EXPECT(ownerCount(env, alice) == 0);
1374 
1375  // alice selects minter as her minter.
1376  env(token::setMinter(alice, minter1));
1377  env.close();
1378 
1379  // A lambda that...
1380  // 1. creates an alice nft
1381  // 2. minted by minter and
1382  // 3. transfers that nft to buyer.
1383  auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1384  std::uint32_t flags) {
1385  uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1386  env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1387  env.close();
1388 
1389  uint256 const offerIndex =
1390  keylet::nftoffer(minter1, env.seq(minter1)).key;
1391  env(token::createOffer(minter1, nftID, XRP(0)),
1392  txflags(tfSellNFToken));
1393  env.close();
1394 
1395  env(token::acceptSellOffer(buyer, offerIndex));
1396  env.close();
1397 
1398  return nftID;
1399  };
1400 
1401  // An NFT without flagBurnable can only be burned by its owner.
1402  {
1403  uint256 const noBurnID = nftToBuyer(0);
1404  env(token::burn(alice, noBurnID),
1405  token::owner(buyer),
1406  ter(tecNO_PERMISSION));
1407  env.close();
1408  env(token::burn(minter1, noBurnID),
1409  token::owner(buyer),
1410  ter(tecNO_PERMISSION));
1411  env.close();
1412  env(token::burn(minter2, noBurnID),
1413  token::owner(buyer),
1414  ter(tecNO_PERMISSION));
1415  env.close();
1416 
1417  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1418  env(token::burn(buyer, noBurnID), token::owner(buyer));
1419  env.close();
1420  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1421  }
1422  // An NFT with flagBurnable can be burned by the issuer.
1423  {
1424  uint256 const burnableID = nftToBuyer(tfBurnable);
1425  env(token::burn(minter2, burnableID),
1426  token::owner(buyer),
1427  ter(tecNO_PERMISSION));
1428  env.close();
1429 
1430  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1431  env(token::burn(alice, burnableID), token::owner(buyer));
1432  env.close();
1433  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1434  }
1435  // An NFT with flagBurnable can be burned by the owner.
1436  {
1437  uint256 const burnableID = nftToBuyer(tfBurnable);
1438  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1439  env(token::burn(buyer, burnableID));
1440  env.close();
1441  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1442  }
1443  // An NFT with flagBurnable can be burned by the minter.
1444  {
1445  uint256 const burnableID = nftToBuyer(tfBurnable);
1446  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1447  env(token::burn(buyer, burnableID), token::owner(buyer));
1448  env.close();
1449  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1450  }
1451  // An nft with flagBurnable may be burned by the issuers' minter,
1452  // who may not be the original minter.
1453  {
1454  uint256 const burnableID = nftToBuyer(tfBurnable);
1455  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1456 
1457  env(token::setMinter(alice, minter2));
1458  env.close();
1459 
1460  // minter1 is no longer alice's minter, so no longer has
1461  // permisson to burn alice's nfts.
1462  env(token::burn(minter1, burnableID),
1463  token::owner(buyer),
1464  ter(tecNO_PERMISSION));
1465  env.close();
1466  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1467 
1468  // minter2, however, can burn alice's nfts.
1469  env(token::burn(minter2, burnableID), token::owner(buyer));
1470  env.close();
1471  BEAST_EXPECT(ownerCount(env, buyer) == 0);
1472  }
1473  }
1474 
1475  void
1477  {
1478  // Exercise NFTs with flagOnlyXRP set and not set.
1479  testcase("Mint flagOnlyXRP");
1480 
1481  using namespace test::jtx;
1482 
1483  Env env{*this, features};
1484  Account const alice{"alice"};
1485  Account const buyer{"buyer"};
1486  Account const gw("gw");
1487  IOU const gwAUD(gw["AUD"]);
1488 
1489  // Set trust lines so alice and buyer can use gwAUD.
1490  env.fund(XRP(1000), alice, buyer, gw);
1491  env.close();
1492  env(trust(alice, gwAUD(1000)));
1493  env(trust(buyer, gwAUD(1000)));
1494  env.close();
1495  env(pay(gw, buyer, gwAUD(100)));
1496 
1497  // Don't set flagOnlyXRP and offers can be made with IOUs.
1498  {
1499  uint256 const nftIOUsOkayID{
1500  token::getNextID(env, alice, 0u, tfTransferable)};
1501  env(token::mint(alice, 0u), txflags(tfTransferable));
1502  env.close();
1503 
1504  BEAST_EXPECT(ownerCount(env, alice) == 2);
1505  uint256 const aliceOfferIndex =
1506  keylet::nftoffer(alice, env.seq(alice)).key;
1507  env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1508  txflags(tfSellNFToken));
1509  env.close();
1510  BEAST_EXPECT(ownerCount(env, alice) == 3);
1511 
1512  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1513  uint256 const buyerOfferIndex =
1514  keylet::nftoffer(buyer, env.seq(buyer)).key;
1515  env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1516  token::owner(alice));
1517  env.close();
1518  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1519 
1520  // Cancel the two offers just to be tidy.
1521  env(token::cancelOffer(alice, {aliceOfferIndex}));
1522  env(token::cancelOffer(buyer, {buyerOfferIndex}));
1523  env.close();
1524  BEAST_EXPECT(ownerCount(env, alice) == 2);
1525  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1526 
1527  // Also burn alice's nft.
1528  env(token::burn(alice, nftIOUsOkayID));
1529  env.close();
1530  BEAST_EXPECT(ownerCount(env, alice) == 1);
1531  }
1532 
1533  // Set flagOnlyXRP and offers using IOUs are rejected.
1534  {
1535  uint256 const nftOnlyXRPID{
1536  token::getNextID(env, alice, 0u, tfOnlyXRP | tfTransferable)};
1537  env(token::mint(alice, 0u), txflags(tfOnlyXRP | tfTransferable));
1538  env.close();
1539 
1540  BEAST_EXPECT(ownerCount(env, alice) == 2);
1541  env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1542  txflags(tfSellNFToken),
1543  ter(temBAD_AMOUNT));
1544  env.close();
1545  BEAST_EXPECT(ownerCount(env, alice) == 2);
1546 
1547  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1548  env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1549  token::owner(alice),
1550  ter(temBAD_AMOUNT));
1551  env.close();
1552  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1553 
1554  // However offers for XRP are okay.
1555  BEAST_EXPECT(ownerCount(env, alice) == 2);
1556  env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1557  txflags(tfSellNFToken));
1558  env.close();
1559  BEAST_EXPECT(ownerCount(env, alice) == 3);
1560 
1561  BEAST_EXPECT(ownerCount(env, buyer) == 1);
1562  env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1563  token::owner(alice));
1564  env.close();
1565  BEAST_EXPECT(ownerCount(env, buyer) == 2);
1566  }
1567  }
1568 
1569  void
1571  {
1572  // Exercise NFTs with flagCreateTrustLines set and not set.
1573  testcase("Mint flagCreateTrustLines");
1574 
1575  using namespace test::jtx;
1576 
1577  Account const alice{"alice"};
1578  Account const becky{"becky"};
1579  Account const cheri{"cheri"};
1580  Account const gw("gw");
1581  IOU const gwAUD(gw["AUD"]);
1582  IOU const gwCAD(gw["CAD"]);
1583  IOU const gwEUR(gw["EUR"]);
1584 
1585  // The behavior of this test changes dramatically based on the
1586  // presence (or absence) of the fixRemoveNFTokenAutoTrustLine
1587  // amendment. So we test both cases here.
1588  for (auto const& tweakedFeatures :
1589  {features - fixRemoveNFTokenAutoTrustLine,
1590  features | fixRemoveNFTokenAutoTrustLine})
1591  {
1592  Env env{*this, tweakedFeatures};
1593  env.fund(XRP(1000), alice, becky, cheri, gw);
1594  env.close();
1595 
1596  // Set trust lines so becky and cheri can use gw's currency.
1597  env(trust(becky, gwAUD(1000)));
1598  env(trust(cheri, gwAUD(1000)));
1599  env(trust(becky, gwCAD(1000)));
1600  env(trust(cheri, gwCAD(1000)));
1601  env(trust(becky, gwEUR(1000)));
1602  env(trust(cheri, gwEUR(1000)));
1603  env.close();
1604  env(pay(gw, becky, gwAUD(500)));
1605  env(pay(gw, becky, gwCAD(500)));
1606  env(pay(gw, becky, gwEUR(500)));
1607  env(pay(gw, cheri, gwAUD(500)));
1608  env(pay(gw, cheri, gwCAD(500)));
1609  env.close();
1610 
1611  // An nft without flagCreateTrustLines but with a non-zero transfer
1612  // fee will not allow creating offers that use IOUs for payment.
1613  for (std::uint32_t xferFee : {0, 1})
1614  {
1615  uint256 const nftNoAutoTrustID{
1616  token::getNextID(env, alice, 0u, tfTransferable, xferFee)};
1617  env(token::mint(alice, 0u),
1618  token::xferFee(xferFee),
1619  txflags(tfTransferable));
1620  env.close();
1621 
1622  // becky buys the nft for 1 drop.
1623  uint256 const beckyBuyOfferIndex =
1624  keylet::nftoffer(becky, env.seq(becky)).key;
1625  env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1626  token::owner(alice));
1627  env.close();
1628  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1629  env.close();
1630 
1631  // becky attempts to sell the nft for AUD.
1632  TER const createOfferTER =
1633  xferFee ? TER(tecNO_LINE) : TER(tesSUCCESS);
1634  uint256 const beckyOfferIndex =
1635  keylet::nftoffer(becky, env.seq(becky)).key;
1636  env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1637  txflags(tfSellNFToken),
1638  ter(createOfferTER));
1639  env.close();
1640 
1641  // cheri offers to buy the nft for CAD.
1642  uint256 const cheriOfferIndex =
1643  keylet::nftoffer(cheri, env.seq(cheri)).key;
1644  env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1645  token::owner(becky),
1646  ter(createOfferTER));
1647  env.close();
1648 
1649  // To keep things tidy, cancel the offers.
1650  env(token::cancelOffer(becky, {beckyOfferIndex}));
1651  env(token::cancelOffer(cheri, {cheriOfferIndex}));
1652  env.close();
1653  }
1654  // An nft with flagCreateTrustLines but with a non-zero transfer
1655  // fee allows transfers using IOUs for payment.
1656  {
1657  std::uint16_t transferFee = 10000; // 10%
1658 
1659  uint256 const nftAutoTrustID{token::getNextID(
1660  env, alice, 0u, tfTransferable | tfTrustLine, transferFee)};
1661 
1662  // If the fixRemoveNFTokenAutoTrustLine amendment is active
1663  // then this transaction fails.
1664  {
1665  TER const mintTER =
1666  tweakedFeatures[fixRemoveNFTokenAutoTrustLine]
1667  ? static_cast<TER>(temINVALID_FLAG)
1668  : static_cast<TER>(tesSUCCESS);
1669 
1670  env(token::mint(alice, 0u),
1671  token::xferFee(transferFee),
1672  txflags(tfTransferable | tfTrustLine),
1673  ter(mintTER));
1674  env.close();
1675 
1676  // If fixRemoveNFTokenAutoTrustLine is active the rest
1677  // of this test falls on its face.
1678  if (tweakedFeatures[fixRemoveNFTokenAutoTrustLine])
1679  break;
1680  }
1681  // becky buys the nft for 1 drop.
1682  uint256 const beckyBuyOfferIndex =
1683  keylet::nftoffer(becky, env.seq(becky)).key;
1684  env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1685  token::owner(alice));
1686  env.close();
1687  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1688  env.close();
1689 
1690  // becky sells the nft for AUD.
1691  uint256 const beckySellOfferIndex =
1692  keylet::nftoffer(becky, env.seq(becky)).key;
1693  env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1694  txflags(tfSellNFToken));
1695  env.close();
1696  env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1697  env.close();
1698 
1699  // alice should now have a trust line for gwAUD.
1700  BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1701 
1702  // becky buys the nft back for CAD.
1703  uint256 const beckyBuyBackOfferIndex =
1704  keylet::nftoffer(becky, env.seq(becky)).key;
1705  env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1706  token::owner(cheri));
1707  env.close();
1708  env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1709  env.close();
1710 
1711  // alice should now have a trust line for gwAUD and gwCAD.
1712  BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1713  BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1714  }
1715  // Now that alice has trust lines preestablished, an nft without
1716  // flagCreateTrustLines will work for preestablished trust lines.
1717  {
1718  std::uint16_t transferFee = 5000; // 5%
1719  uint256 const nftNoAutoTrustID{token::getNextID(
1720  env, alice, 0u, tfTransferable, transferFee)};
1721  env(token::mint(alice, 0u),
1722  token::xferFee(transferFee),
1723  txflags(tfTransferable));
1724  env.close();
1725 
1726  // alice sells the nft using AUD.
1727  uint256 const aliceSellOfferIndex =
1728  keylet::nftoffer(alice, env.seq(alice)).key;
1729  env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1730  txflags(tfSellNFToken));
1731  env.close();
1732  env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1733  env.close();
1734 
1735  // alice should now have AUD(210):
1736  // o 200 for this sale and
1737  // o 10 for the previous sale's fee.
1738  BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1739 
1740  // cheri can't sell the NFT for EUR, but can for CAD.
1741  env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1742  txflags(tfSellNFToken),
1743  ter(tecNO_LINE));
1744  env.close();
1745  uint256 const cheriSellOfferIndex =
1746  keylet::nftoffer(cheri, env.seq(cheri)).key;
1747  env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1748  txflags(tfSellNFToken));
1749  env.close();
1750  env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1751  env.close();
1752 
1753  // alice should now have CAD(10):
1754  // o 5 from this sale's fee and
1755  // o 5 for the previous sale's fee.
1756  BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1757  }
1758  }
1759  }
1760 
1761  void
1763  {
1764  // Exercise NFTs with flagTransferable set and not set.
1765  testcase("Mint flagTransferable");
1766 
1767  using namespace test::jtx;
1768 
1769  Env env{*this, features};
1770 
1771  Account const alice{"alice"};
1772  Account const becky{"becky"};
1773  Account const minter{"minter"};
1774 
1775  env.fund(XRP(1000), alice, becky, minter);
1776  env.close();
1777 
1778  // First try an nft made by alice without flagTransferable set.
1779  {
1780  BEAST_EXPECT(ownerCount(env, alice) == 0);
1781  uint256 const nftAliceNoTransferID{
1782  token::getNextID(env, alice, 0u)};
1783  env(token::mint(alice, 0u), token::xferFee(0));
1784  env.close();
1785  BEAST_EXPECT(ownerCount(env, alice) == 1);
1786 
1787  // becky tries to offer to buy alice's nft.
1788  BEAST_EXPECT(ownerCount(env, becky) == 0);
1789  env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1790  token::owner(alice),
1792 
1793  // alice offers to sell the nft and becky accepts the offer.
1794  uint256 const aliceSellOfferIndex =
1795  keylet::nftoffer(alice, env.seq(alice)).key;
1796  env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1797  txflags(tfSellNFToken));
1798  env.close();
1799  env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1800  env.close();
1801  BEAST_EXPECT(ownerCount(env, alice) == 0);
1802  BEAST_EXPECT(ownerCount(env, becky) == 1);
1803 
1804  // becky tries to offer the nft for sale.
1805  env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1806  txflags(tfSellNFToken),
1808  env.close();
1809  BEAST_EXPECT(ownerCount(env, alice) == 0);
1810  BEAST_EXPECT(ownerCount(env, becky) == 1);
1811 
1812  // becky tries to offer the nft for sale with alice as the
1813  // destination. That also doesn't work.
1814  env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1815  txflags(tfSellNFToken),
1816  token::destination(alice),
1818  env.close();
1819  BEAST_EXPECT(ownerCount(env, alice) == 0);
1820  BEAST_EXPECT(ownerCount(env, becky) == 1);
1821 
1822  // alice offers to buy the nft back from becky. becky accepts
1823  // the offer.
1824  uint256 const aliceBuyOfferIndex =
1825  keylet::nftoffer(alice, env.seq(alice)).key;
1826  env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1827  token::owner(becky));
1828  env.close();
1829  env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1830  env.close();
1831  BEAST_EXPECT(ownerCount(env, alice) == 1);
1832  BEAST_EXPECT(ownerCount(env, becky) == 0);
1833 
1834  // alice burns her nft so accounting is simpler below.
1835  env(token::burn(alice, nftAliceNoTransferID));
1836  env.close();
1837  BEAST_EXPECT(ownerCount(env, alice) == 0);
1838  BEAST_EXPECT(ownerCount(env, becky) == 0);
1839  }
1840  // Try an nft minted by minter for alice without flagTransferable set.
1841  {
1842  env(token::setMinter(alice, minter));
1843  env.close();
1844 
1845  BEAST_EXPECT(ownerCount(env, minter) == 0);
1846  uint256 const nftMinterNoTransferID{
1847  token::getNextID(env, alice, 0u)};
1848  env(token::mint(minter), token::issuer(alice));
1849  env.close();
1850  BEAST_EXPECT(ownerCount(env, minter) == 1);
1851 
1852  // becky tries to offer to buy minter's nft.
1853  BEAST_EXPECT(ownerCount(env, becky) == 0);
1854  env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1855  token::owner(minter),
1857  env.close();
1858  BEAST_EXPECT(ownerCount(env, becky) == 0);
1859 
1860  // alice removes authorization of minter.
1861  env(token::clearMinter(alice));
1862  env.close();
1863 
1864  // minter tries to offer their nft for sale.
1865  BEAST_EXPECT(ownerCount(env, minter) == 1);
1866  env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1867  txflags(tfSellNFToken),
1869  env.close();
1870  BEAST_EXPECT(ownerCount(env, minter) == 1);
1871 
1872  // Let enough ledgers pass that old transactions are no longer
1873  // retried, then alice gives authorization back to minter.
1874  for (int i = 0; i < 10; ++i)
1875  env.close();
1876 
1877  env(token::setMinter(alice, minter));
1878  env.close();
1879  BEAST_EXPECT(ownerCount(env, minter) == 1);
1880 
1881  // minter successfully offers their nft for sale.
1882  BEAST_EXPECT(ownerCount(env, minter) == 1);
1883  uint256 const minterSellOfferIndex =
1884  keylet::nftoffer(minter, env.seq(minter)).key;
1885  env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1886  txflags(tfSellNFToken));
1887  env.close();
1888  BEAST_EXPECT(ownerCount(env, minter) == 2);
1889 
1890  // alice removes authorization of minter so we can see whether
1891  // minter's pre-existing offer still works.
1892  env(token::clearMinter(alice));
1893  env.close();
1894 
1895  // becky buys minter's nft even though minter is no longer alice's
1896  // official minter.
1897  BEAST_EXPECT(ownerCount(env, becky) == 0);
1898  env(token::acceptSellOffer(becky, minterSellOfferIndex));
1899  env.close();
1900  BEAST_EXPECT(ownerCount(env, becky) == 1);
1901  BEAST_EXPECT(ownerCount(env, minter) == 0);
1902 
1903  // becky attempts to sell the nft.
1904  env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1905  txflags(tfSellNFToken),
1907  env.close();
1908 
1909  // Since minter is not, at the moment, alice's official minter
1910  // they cannot create an offer to buy the nft they minted.
1911  BEAST_EXPECT(ownerCount(env, minter) == 0);
1912  env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1913  token::owner(becky),
1915  env.close();
1916  BEAST_EXPECT(ownerCount(env, minter) == 0);
1917 
1918  // alice can create an offer to buy the nft.
1919  BEAST_EXPECT(ownerCount(env, alice) == 0);
1920  uint256 const aliceBuyOfferIndex =
1921  keylet::nftoffer(alice, env.seq(alice)).key;
1922  env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1923  token::owner(becky));
1924  env.close();
1925  BEAST_EXPECT(ownerCount(env, alice) == 1);
1926 
1927  // Let enough ledgers pass that old transactions are no longer
1928  // retried, then alice gives authorization back to minter.
1929  for (int i = 0; i < 10; ++i)
1930  env.close();
1931 
1932  env(token::setMinter(alice, minter));
1933  env.close();
1934 
1935  // Now minter can create an offer to buy the nft.
1936  BEAST_EXPECT(ownerCount(env, minter) == 0);
1937  uint256 const minterBuyOfferIndex =
1938  keylet::nftoffer(minter, env.seq(minter)).key;
1939  env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1940  token::owner(becky));
1941  env.close();
1942  BEAST_EXPECT(ownerCount(env, minter) == 1);
1943 
1944  // alice removes authorization of minter so we can see whether
1945  // minter's pre-existing buy offer still works.
1946  env(token::clearMinter(alice));
1947  env.close();
1948 
1949  // becky accepts minter's sell offer.
1950  BEAST_EXPECT(ownerCount(env, minter) == 1);
1951  BEAST_EXPECT(ownerCount(env, becky) == 1);
1952  env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1953  env.close();
1954  BEAST_EXPECT(ownerCount(env, minter) == 1);
1955  BEAST_EXPECT(ownerCount(env, becky) == 0);
1956  BEAST_EXPECT(ownerCount(env, alice) == 1);
1957 
1958  // minter burns their nft and alice cancels her offer so the
1959  // next tests can start with a clean slate.
1960  env(token::burn(minter, nftMinterNoTransferID), ter(tesSUCCESS));
1961  env.close();
1962  env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1963  env.close();
1964  BEAST_EXPECT(ownerCount(env, alice) == 0);
1965  BEAST_EXPECT(ownerCount(env, becky) == 0);
1966  BEAST_EXPECT(ownerCount(env, minter) == 0);
1967  }
1968  // nfts with flagTransferable set should be buyable and salable
1969  // by anybody.
1970  {
1971  BEAST_EXPECT(ownerCount(env, alice) == 0);
1972  uint256 const nftAliceID{
1973  token::getNextID(env, alice, 0u, tfTransferable)};
1974  env(token::mint(alice, 0u), txflags(tfTransferable));
1975  env.close();
1976  BEAST_EXPECT(ownerCount(env, alice) == 1);
1977 
1978  // Both alice and becky can make offers for alice's nft.
1979  uint256 const aliceSellOfferIndex =
1980  keylet::nftoffer(alice, env.seq(alice)).key;
1981  env(token::createOffer(alice, nftAliceID, XRP(20)),
1982  txflags(tfSellNFToken));
1983  env.close();
1984  BEAST_EXPECT(ownerCount(env, alice) == 2);
1985 
1986  uint256 const beckyBuyOfferIndex =
1987  keylet::nftoffer(becky, env.seq(becky)).key;
1988  env(token::createOffer(becky, nftAliceID, XRP(21)),
1989  token::owner(alice));
1990  env.close();
1991  BEAST_EXPECT(ownerCount(env, alice) == 2);
1992 
1993  // becky accepts alice's sell offer.
1994  env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1995  env.close();
1996  BEAST_EXPECT(ownerCount(env, alice) == 0);
1997  BEAST_EXPECT(ownerCount(env, becky) == 2);
1998 
1999  // becky offers to sell the nft.
2000  uint256 const beckySellOfferIndex =
2001  keylet::nftoffer(becky, env.seq(becky)).key;
2002  env(token::createOffer(becky, nftAliceID, XRP(22)),
2003  txflags(tfSellNFToken));
2004  env.close();
2005  BEAST_EXPECT(ownerCount(env, alice) == 0);
2006  BEAST_EXPECT(ownerCount(env, becky) == 3);
2007 
2008  // minter buys the nft (even though minter is not currently
2009  // alice's minter).
2010  env(token::acceptSellOffer(minter, beckySellOfferIndex));
2011  env.close();
2012  BEAST_EXPECT(ownerCount(env, alice) == 0);
2013  BEAST_EXPECT(ownerCount(env, becky) == 1);
2014  BEAST_EXPECT(ownerCount(env, minter) == 1);
2015 
2016  // minter offers to sell the nft.
2017  uint256 const minterSellOfferIndex =
2018  keylet::nftoffer(minter, env.seq(minter)).key;
2019  env(token::createOffer(minter, nftAliceID, XRP(23)),
2020  txflags(tfSellNFToken));
2021  env.close();
2022  BEAST_EXPECT(ownerCount(env, alice) == 0);
2023  BEAST_EXPECT(ownerCount(env, becky) == 1);
2024  BEAST_EXPECT(ownerCount(env, minter) == 2);
2025 
2026  // alice buys back the nft.
2027  env(token::acceptSellOffer(alice, minterSellOfferIndex));
2028  env.close();
2029  BEAST_EXPECT(ownerCount(env, alice) == 1);
2030  BEAST_EXPECT(ownerCount(env, becky) == 1);
2031  BEAST_EXPECT(ownerCount(env, minter) == 0);
2032 
2033  // Remember the buy offer that becky made for alice's token way
2034  // back when? It's still in the ledger, and alice accepts it.
2035  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2036  env.close();
2037  BEAST_EXPECT(ownerCount(env, alice) == 0);
2038  BEAST_EXPECT(ownerCount(env, becky) == 1);
2039  BEAST_EXPECT(ownerCount(env, minter) == 0);
2040 
2041  // Just for tidyness, becky burns the token before shutting
2042  // things down.
2043  env(token::burn(becky, nftAliceID));
2044  env.close();
2045  BEAST_EXPECT(ownerCount(env, alice) == 0);
2046  BEAST_EXPECT(ownerCount(env, becky) == 0);
2047  BEAST_EXPECT(ownerCount(env, minter) == 0);
2048  }
2049  }
2050 
2051  void
2053  {
2054  // Exercise NFTs with and without a transferFee.
2055  testcase("Mint transferFee");
2056 
2057  using namespace test::jtx;
2058 
2059  Env env{*this, features};
2060 
2061  Account const alice{"alice"};
2062  Account const becky{"becky"};
2063  Account const carol{"carol"};
2064  Account const minter{"minter"};
2065  Account const gw{"gw"};
2066  IOU const gwXAU(gw["XAU"]);
2067 
2068  env.fund(XRP(1000), alice, becky, carol, minter, gw);
2069  env.close();
2070 
2071  env(trust(alice, gwXAU(2000)));
2072  env(trust(becky, gwXAU(2000)));
2073  env(trust(carol, gwXAU(2000)));
2074  env(trust(minter, gwXAU(2000)));
2075  env.close();
2076  env(pay(gw, alice, gwXAU(1000)));
2077  env(pay(gw, becky, gwXAU(1000)));
2078  env(pay(gw, carol, gwXAU(1000)));
2079  env(pay(gw, minter, gwXAU(1000)));
2080  env.close();
2081 
2082  // Giving alice a minter helps us see if transfer rates are affected
2083  // by that.
2084  env(token::setMinter(alice, minter));
2085  env.close();
2086 
2087  // If there is no transferFee, then alice gets nothing for the
2088  // transfer.
2089  {
2090  BEAST_EXPECT(ownerCount(env, alice) == 1);
2091  BEAST_EXPECT(ownerCount(env, becky) == 1);
2092  BEAST_EXPECT(ownerCount(env, carol) == 1);
2093  BEAST_EXPECT(ownerCount(env, minter) == 1);
2094 
2095  uint256 const nftID =
2096  token::getNextID(env, alice, 0u, tfTransferable);
2097  env(token::mint(alice), txflags(tfTransferable));
2098  env.close();
2099 
2100  // Becky buys the nft for XAU(10). Check balances.
2101  uint256 const beckyBuyOfferIndex =
2102  keylet::nftoffer(becky, env.seq(becky)).key;
2103  env(token::createOffer(becky, nftID, gwXAU(10)),
2104  token::owner(alice));
2105  env.close();
2106  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2107  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2108 
2109  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2110  env.close();
2111  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2112  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2113 
2114  // becky sells nft to carol. alice's balance should not change.
2115  uint256 const beckySellOfferIndex =
2116  keylet::nftoffer(becky, env.seq(becky)).key;
2117  env(token::createOffer(becky, nftID, gwXAU(10)),
2118  txflags(tfSellNFToken));
2119  env.close();
2120  env(token::acceptSellOffer(carol, beckySellOfferIndex));
2121  env.close();
2122  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2123  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2124  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2125 
2126  // minter buys nft from carol. alice's balance should not change.
2127  uint256 const minterBuyOfferIndex =
2128  keylet::nftoffer(minter, env.seq(minter)).key;
2129  env(token::createOffer(minter, nftID, gwXAU(10)),
2130  token::owner(carol));
2131  env.close();
2132  env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
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(1000));
2137  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2138 
2139  // minter sells the nft to alice. gwXAU balances should finish
2140  // where they started.
2141  uint256 const minterSellOfferIndex =
2142  keylet::nftoffer(minter, env.seq(minter)).key;
2143  env(token::createOffer(minter, nftID, gwXAU(10)),
2144  txflags(tfSellNFToken));
2145  env.close();
2146  env(token::acceptSellOffer(alice, minterSellOfferIndex));
2147  env.close();
2148  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2149  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2150  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2151  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2152 
2153  // alice burns the nft to make later tests easier to think about.
2154  env(token::burn(alice, nftID));
2155  env.close();
2156  BEAST_EXPECT(ownerCount(env, alice) == 1);
2157  BEAST_EXPECT(ownerCount(env, becky) == 1);
2158  BEAST_EXPECT(ownerCount(env, carol) == 1);
2159  BEAST_EXPECT(ownerCount(env, minter) == 1);
2160  }
2161 
2162  // Set the smallest possible transfer fee.
2163  {
2164  // An nft with a transfer fee of 1 basis point.
2165  uint256 const nftID =
2166  token::getNextID(env, alice, 0u, tfTransferable, 1);
2167  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2168  env.close();
2169 
2170  // Becky buys the nft for XAU(10). Check balances.
2171  uint256 const beckyBuyOfferIndex =
2172  keylet::nftoffer(becky, env.seq(becky)).key;
2173  env(token::createOffer(becky, nftID, gwXAU(10)),
2174  token::owner(alice));
2175  env.close();
2176  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2177  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2178 
2179  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2180  env.close();
2181  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2182  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2183 
2184  // becky sells nft to carol. alice's balance goes up.
2185  uint256 const beckySellOfferIndex =
2186  keylet::nftoffer(becky, env.seq(becky)).key;
2187  env(token::createOffer(becky, nftID, gwXAU(10)),
2188  txflags(tfSellNFToken));
2189  env.close();
2190  env(token::acceptSellOffer(carol, beckySellOfferIndex));
2191  env.close();
2192 
2193  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2194  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2195  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2196 
2197  // minter buys nft from carol. alice's balance goes up.
2198  uint256 const minterBuyOfferIndex =
2199  keylet::nftoffer(minter, env.seq(minter)).key;
2200  env(token::createOffer(minter, nftID, gwXAU(10)),
2201  token::owner(carol));
2202  env.close();
2203  env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2204  env.close();
2205 
2206  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2207  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2208  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2209  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2210 
2211  // minter sells the nft to alice. Because alice is part of the
2212  // transaction no tranfer fee is removed.
2213  uint256 const minterSellOfferIndex =
2214  keylet::nftoffer(minter, env.seq(minter)).key;
2215  env(token::createOffer(minter, nftID, gwXAU(10)),
2216  txflags(tfSellNFToken));
2217  env.close();
2218  env(token::acceptSellOffer(alice, minterSellOfferIndex));
2219  env.close();
2220  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2221  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2222  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2223  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2224 
2225  // alice pays to becky and carol so subsequent tests are easier
2226  // to think about.
2227  env(pay(alice, becky, gwXAU(0.0001)));
2228  env(pay(alice, carol, gwXAU(0.0001)));
2229  env.close();
2230 
2231  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2232  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2233  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2234  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2235 
2236  // alice burns the nft to make later tests easier to think about.
2237  env(token::burn(alice, nftID));
2238  env.close();
2239  BEAST_EXPECT(ownerCount(env, alice) == 1);
2240  BEAST_EXPECT(ownerCount(env, becky) == 1);
2241  BEAST_EXPECT(ownerCount(env, carol) == 1);
2242  BEAST_EXPECT(ownerCount(env, minter) == 1);
2243  }
2244 
2245  // Set the largest allowed transfer fee.
2246  {
2247  // A transfer fee greater than 50% is not allowed.
2248  env(token::mint(alice),
2249  txflags(tfTransferable),
2250  token::xferFee(maxTransferFee + 1),
2252  env.close();
2253 
2254  // Make an nft with a transfer fee of 50%.
2255  uint256 const nftID = token::getNextID(
2256  env, alice, 0u, tfTransferable, maxTransferFee);
2257  env(token::mint(alice),
2258  txflags(tfTransferable),
2259  token::xferFee(maxTransferFee));
2260  env.close();
2261 
2262  // Becky buys the nft for XAU(10). Check balances.
2263  uint256 const beckyBuyOfferIndex =
2264  keylet::nftoffer(becky, env.seq(becky)).key;
2265  env(token::createOffer(becky, nftID, gwXAU(10)),
2266  token::owner(alice));
2267  env.close();
2268  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2269  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2270 
2271  env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2272  env.close();
2273  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2274  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2275 
2276  // becky sells nft to minter. alice's balance goes up.
2277  uint256 const beckySellOfferIndex =
2278  keylet::nftoffer(becky, env.seq(becky)).key;
2279  env(token::createOffer(becky, nftID, gwXAU(100)),
2280  txflags(tfSellNFToken));
2281  env.close();
2282  env(token::acceptSellOffer(minter, beckySellOfferIndex));
2283  env.close();
2284 
2285  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2286  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2287  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2288 
2289  // carol buys nft from minter. alice's balance goes up.
2290  uint256 const carolBuyOfferIndex =
2291  keylet::nftoffer(carol, env.seq(carol)).key;
2292  env(token::createOffer(carol, nftID, gwXAU(10)),
2293  token::owner(minter));
2294  env.close();
2295  env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2296  env.close();
2297 
2298  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2299  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2300  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2301  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2302 
2303  // carol sells the nft to alice. Because alice is part of the
2304  // transaction no tranfer fee is removed.
2305  uint256 const carolSellOfferIndex =
2306  keylet::nftoffer(carol, env.seq(carol)).key;
2307  env(token::createOffer(carol, nftID, gwXAU(10)),
2308  txflags(tfSellNFToken));
2309  env.close();
2310  env(token::acceptSellOffer(alice, carolSellOfferIndex));
2311  env.close();
2312 
2313  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2314  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2315  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2316  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2317 
2318  // rebalance so subsequent tests are easier to think about.
2319  env(pay(alice, minter, gwXAU(55)));
2320  env(pay(becky, minter, gwXAU(40)));
2321  env.close();
2322  BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2323  BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2324  BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2325  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2326 
2327  // alice burns the nft to make later tests easier to think about.
2328  env(token::burn(alice, nftID));
2329  env.close();
2330  BEAST_EXPECT(ownerCount(env, alice) == 1);
2331  BEAST_EXPECT(ownerCount(env, becky) == 1);
2332  BEAST_EXPECT(ownerCount(env, carol) == 1);
2333  BEAST_EXPECT(ownerCount(env, minter) == 1);
2334  }
2335 
2336  // See the impact of rounding when the nft is sold for small amounts
2337  // of drops.
2338  {
2339  // An nft with a transfer fee of 1 basis point.
2340  uint256 const nftID =
2341  token::getNextID(env, alice, 0u, tfTransferable, 1);
2342  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2343  env.close();
2344 
2345  // minter buys the nft for XRP(1). Since the transfer involves
2346  // alice there should be no transfer fee.
2347  STAmount fee = drops(10);
2348  STAmount aliceBalance = env.balance(alice);
2349  STAmount minterBalance = env.balance(minter);
2350  uint256 const minterBuyOfferIndex =
2351  keylet::nftoffer(minter, env.seq(minter)).key;
2352  env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2353  env.close();
2354  env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2355  env.close();
2356  aliceBalance += XRP(1) - fee;
2357  minterBalance -= XRP(1) + fee;
2358  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2359  BEAST_EXPECT(env.balance(minter) == minterBalance);
2360 
2361  // minter sells to carol. The payment is just small enough that
2362  // alice does not get any transfer fee.
2363  STAmount carolBalance = env.balance(carol);
2364  uint256 const minterSellOfferIndex =
2365  keylet::nftoffer(minter, env.seq(minter)).key;
2366  env(token::createOffer(minter, nftID, drops(99999)),
2367  txflags(tfSellNFToken));
2368  env.close();
2369  env(token::acceptSellOffer(carol, minterSellOfferIndex));
2370  env.close();
2371  minterBalance += drops(99999) - fee;
2372  carolBalance -= drops(99999) + fee;
2373  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2374  BEAST_EXPECT(env.balance(minter) == minterBalance);
2375  BEAST_EXPECT(env.balance(carol) == carolBalance);
2376 
2377  // carol sells to becky. This is the smallest amount to pay for a
2378  // transfer that enables a transfer fee of 1 basis point.
2379  STAmount beckyBalance = env.balance(becky);
2380  uint256 const beckyBuyOfferIndex =
2381  keylet::nftoffer(becky, env.seq(becky)).key;
2382  env(token::createOffer(becky, nftID, drops(100000)),
2383  token::owner(carol));
2384  env.close();
2385  env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2386  env.close();
2387  carolBalance += drops(99999) - fee;
2388  beckyBalance -= drops(100000) + fee;
2389  aliceBalance += drops(1);
2390 
2391  BEAST_EXPECT(env.balance(alice) == aliceBalance);
2392  BEAST_EXPECT(env.balance(minter) == minterBalance);
2393  BEAST_EXPECT(env.balance(carol) == carolBalance);
2394  BEAST_EXPECT(env.balance(becky) == beckyBalance);
2395  }
2396 
2397  // See the impact of rounding when the nft is sold for small amounts
2398  // of an IOU.
2399  {
2400  // An nft with a transfer fee of 1 basis point.
2401  uint256 const nftID =
2402  token::getNextID(env, alice, 0u, tfTransferable, 1);
2403  env(token::mint(alice), txflags(tfTransferable), token::xferFee(1));
2404  env.close();
2405 
2406  // Due to the floating point nature of IOUs we need to
2407  // significantly reduce the gwXAU balances of our accounts prior
2408  // to the iou transfer. Otherwise no transfers will happen.
2409  env(pay(alice, gw, env.balance(alice, gwXAU)));
2410  env(pay(minter, gw, env.balance(minter, gwXAU)));
2411  env(pay(becky, gw, env.balance(becky, gwXAU)));
2412  env.close();
2413 
2414  STAmount const startXAUBalance(
2415  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2416  env(pay(gw, alice, startXAUBalance));
2417  env(pay(gw, minter, startXAUBalance));
2418  env(pay(gw, becky, startXAUBalance));
2419  env.close();
2420 
2421  // Here is the smallest expressible gwXAU amount.
2422  STAmount tinyXAU(
2423  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset);
2424 
2425  // minter buys the nft for tinyXAU. Since the transfer involves
2426  // alice there should be no transfer fee.
2427  STAmount aliceBalance = env.balance(alice, gwXAU);
2428  STAmount minterBalance = env.balance(minter, gwXAU);
2429  uint256 const minterBuyOfferIndex =
2430  keylet::nftoffer(minter, env.seq(minter)).key;
2431  env(token::createOffer(minter, nftID, tinyXAU),
2432  token::owner(alice));
2433  env.close();
2434  env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2435  env.close();
2436  aliceBalance += tinyXAU;
2437  minterBalance -= tinyXAU;
2438  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2439  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2440 
2441  // minter sells to carol.
2442  STAmount carolBalance = env.balance(carol, gwXAU);
2443  uint256 const minterSellOfferIndex =
2444  keylet::nftoffer(minter, env.seq(minter)).key;
2445  env(token::createOffer(minter, nftID, tinyXAU),
2446  txflags(tfSellNFToken));
2447  env.close();
2448  env(token::acceptSellOffer(carol, minterSellOfferIndex));
2449  env.close();
2450 
2451  minterBalance += tinyXAU;
2452  carolBalance -= tinyXAU;
2453  // tiny XAU is so small that alice does not get a transfer fee.
2454  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2455  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2456  BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2457 
2458  // carol sells to becky. This is the smallest gwXAU amount
2459  // to pay for a transfer that enables a transfer fee of 1.
2460  STAmount const cheapNFT(
2461  gwXAU.issue(), STAmount::cMinValue, STAmount::cMinOffset + 5);
2462 
2463  STAmount beckyBalance = env.balance(becky, gwXAU);
2464  uint256 const beckyBuyOfferIndex =
2465  keylet::nftoffer(becky, env.seq(becky)).key;
2466  env(token::createOffer(becky, nftID, cheapNFT),
2467  token::owner(carol));
2468  env.close();
2469  env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2470  env.close();
2471 
2472  aliceBalance += tinyXAU;
2473  beckyBalance -= cheapNFT;
2474  carolBalance += cheapNFT - tinyXAU;
2475  BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2476  BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2477  BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2478  BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2479  }
2480  }
2481 
2482  void
2484  {
2485  // Exercise the NFT taxon field.
2486  testcase("Mint taxon");
2487 
2488  using namespace test::jtx;
2489 
2490  Env env{*this, features};
2491 
2492  Account const alice{"alice"};
2493  Account const becky{"becky"};
2494 
2495  env.fund(XRP(1000), alice, becky);
2496  env.close();
2497 
2498  // The taxon field is incorporated straight into the NFT ID. So
2499  // tests only need to operate on NFT IDs; we don't need to generate
2500  // any transactions.
2501 
2502  // The taxon value should be recoverable from the NFT ID.
2503  {
2504  uint256 const nftID = token::getNextID(env, alice, 0u);
2505  BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon(0));
2506  }
2507 
2508  // Make sure the full range of taxon values work. We just tried
2509  // the minimum. Now try the largest.
2510  {
2511  uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2512  BEAST_EXPECT(nft::getTaxon(nftID) == nft::toTaxon((0xFFFFFFFF)));
2513  }
2514 
2515  // Do some touch testing to show that the taxon is recoverable no
2516  // matter what else changes around it in the nft ID.
2517  {
2518  std::uint32_t const taxon = rand_int<std::uint32_t>();
2519  for (int i = 0; i < 10; ++i)
2520  {
2521  // lambda to produce a useful message on error.
2522  auto check = [this](std::uint32_t taxon, uint256 const& nftID) {
2523  nft::Taxon const gotTaxon = nft::getTaxon(nftID);
2524  if (nft::toTaxon(taxon) == gotTaxon)
2525  pass();
2526  else
2527  {
2528  std::stringstream ss;
2529  ss << "Taxon recovery failed from nftID "
2530  << to_string(nftID) << ". Expected: " << taxon
2531  << "; got: " << gotTaxon;
2532  fail(ss.str());
2533  }
2534  };
2535 
2536  uint256 const nftAliceID = token::getID(
2537  alice,
2538  taxon,
2539  rand_int<std::uint32_t>(),
2540  rand_int<std::uint16_t>(),
2541  rand_int<std::uint16_t>());
2542  check(taxon, nftAliceID);
2543 
2544  uint256 const nftBeckyID = token::getID(
2545  becky,
2546  taxon,
2547  rand_int<std::uint32_t>(),
2548  rand_int<std::uint16_t>(),
2549  rand_int<std::uint16_t>());
2550  check(taxon, nftBeckyID);
2551  }
2552  }
2553  }
2554 
2555  void
2557  {
2558  // Exercise the NFT URI field.
2559  // 1. Create a number of NFTs with and without URIs.
2560  // 2. Retrieve the NFTs from the server.
2561  // 3. Make sure the right URI is attached to each NFT.
2562  testcase("Mint URI");
2563 
2564  using namespace test::jtx;
2565 
2566  Env env{*this, features};
2567 
2568  Account const alice{"alice"};
2569  Account const becky{"becky"};
2570 
2571  env.fund(XRP(10000), alice, becky);
2572  env.close();
2573 
2574  // lambda that returns a randomly generated string which fits
2575  // the constraints of a URI. Empty strings may be returned.
2576  // In the empty string case do not add the URI to the nft.
2577  auto randURI = []() {
2578  std::string ret;
2579 
2580  // About 20% of the returned strings should be empty
2581  if (rand_int(4) == 0)
2582  return ret;
2583 
2584  std::size_t const strLen = rand_int(256);
2585  ret.reserve(strLen);
2586  for (std::size_t i = 0; i < strLen; ++i)
2587  ret.push_back(rand_byte());
2588 
2589  return ret;
2590  };
2591 
2592  // Make a list of URIs that we'll put in nfts.
2593  struct Entry
2594  {
2595  std::string uri;
2596  std::uint32_t taxon;
2597 
2598  Entry(std::string uri_, std::uint32_t taxon_)
2599  : uri(std::move(uri_)), taxon(taxon_)
2600  {
2601  }
2602  };
2603 
2604  std::vector<Entry> entries;
2605  entries.reserve(100);
2606  for (std::size_t i = 0; i < 100; ++i)
2607  entries.emplace_back(randURI(), rand_int<std::uint32_t>());
2608 
2609  // alice creates nfts using entries.
2610  for (Entry const& entry : entries)
2611  {
2612  if (entry.uri.empty())
2613  {
2614  env(token::mint(alice, entry.taxon));
2615  }
2616  else
2617  {
2618  env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2619  }
2620  env.close();
2621  }
2622 
2623  // Recover alice's nfts from the ledger.
2624  Json::Value aliceNFTs = [&env, &alice]() {
2625  Json::Value params;
2626  params[jss::account] = alice.human();
2627  params[jss::type] = "state";
2628  return env.rpc("json", "account_nfts", to_string(params));
2629  }();
2630 
2631  // Verify that the returned NFTs match what we sent.
2632  Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2633  if (!BEAST_EXPECT(nfts.size() == entries.size()))
2634  return;
2635 
2636  // Sort the returned NFTs by nft_serial so the are in the same order
2637  // as entries.
2638  std::vector<Json::Value> sortedNFTs;
2639  sortedNFTs.reserve(nfts.size());
2640  for (std::size_t i = 0; i < nfts.size(); ++i)
2641  sortedNFTs.push_back(nfts[i]);
2642  std::sort(
2643  sortedNFTs.begin(),
2644  sortedNFTs.end(),
2645  [](Json::Value const& lhs, Json::Value const& rhs) {
2646  return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2647  });
2648 
2649  for (std::size_t i = 0; i < entries.size(); ++i)
2650  {
2651  Entry const& entry = entries[i];
2652  Json::Value const& ret = sortedNFTs[i];
2653  BEAST_EXPECT(entry.taxon == ret[sfNFTokenTaxon.jsonName]);
2654  if (entry.uri.empty())
2655  {
2656  BEAST_EXPECT(!ret.isMember(sfURI.jsonName));
2657  }
2658  else
2659  {
2660  BEAST_EXPECT(strHex(entry.uri) == ret[sfURI.jsonName]);
2661  }
2662  }
2663  }
2664 
2665  void
2667  {
2668  // Explore the CreateOffer Destination field.
2669  testcase("Create offer destination");
2670 
2671  using namespace test::jtx;
2672 
2673  Env env{*this, features};
2674 
2675  Account const issuer{"issuer"};
2676  Account const minter{"minter"};
2677  Account const buyer{"buyer"};
2678  Account const broker{"broker"};
2679 
2680  env.fund(XRP(1000), issuer, minter, buyer, broker);
2681 
2682  // We want to explore how issuers vs minters fits into the permission
2683  // scheme. So issuer issues and minter mints.
2684  env(token::setMinter(issuer, minter));
2685  env.close();
2686 
2687  uint256 const nftokenID =
2688  token::getNextID(env, issuer, 0, tfTransferable);
2689  env(token::mint(minter, 0),
2690  token::issuer(issuer),
2691  txflags(tfTransferable));
2692  env.close();
2693 
2694  // Test how adding a Destination field to an offer affects permissions
2695  // for canceling offers.
2696  {
2697  uint256 const offerMinterToIssuer =
2698  keylet::nftoffer(minter, env.seq(minter)).key;
2699  env(token::createOffer(minter, nftokenID, drops(1)),
2700  token::destination(issuer),
2701  txflags(tfSellNFToken));
2702 
2703  uint256 const offerMinterToBuyer =
2704  keylet::nftoffer(minter, env.seq(minter)).key;
2705  env(token::createOffer(minter, nftokenID, drops(1)),
2706  token::destination(buyer),
2707  txflags(tfSellNFToken));
2708 
2709  uint256 const offerIssuerToMinter =
2710  keylet::nftoffer(issuer, env.seq(issuer)).key;
2711  env(token::createOffer(issuer, nftokenID, drops(1)),
2712  token::owner(minter),
2713  token::destination(minter));
2714 
2715  uint256 const offerIssuerToBuyer =
2716  keylet::nftoffer(issuer, env.seq(issuer)).key;
2717  env(token::createOffer(issuer, nftokenID, drops(1)),
2718  token::owner(minter),
2719  token::destination(buyer));
2720 
2721  env.close();
2722  BEAST_EXPECT(ownerCount(env, issuer) == 2);
2723  BEAST_EXPECT(ownerCount(env, minter) == 3);
2724  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2725 
2726  // Test who gets to cancel the offers. Anyone outside of the
2727  // offer-owner/destination pair should not be able to cancel the
2728  // offers.
2729  //
2730  // Note that issuer does not have any special permissions regarding
2731  // offer cancellation. issuer cannot cancel an offer for an
2732  // NFToken they issued.
2733  env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2734  ter(tecNO_PERMISSION));
2735  env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2736  ter(tecNO_PERMISSION));
2737  env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2738  ter(tecNO_PERMISSION));
2739  env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2740  ter(tecNO_PERMISSION));
2741  env.close();
2742  BEAST_EXPECT(ownerCount(env, issuer) == 2);
2743  BEAST_EXPECT(ownerCount(env, minter) == 3);
2744  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2745 
2746  // Both the offer creator and and destination should be able to
2747  // cancel the offers.
2748  env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2749  env(token::cancelOffer(minter, {offerMinterToIssuer}));
2750  env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2751  env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2752  env.close();
2753  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2754  BEAST_EXPECT(ownerCount(env, minter) == 1);
2755  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2756  }
2757 
2758  // Test how adding a Destination field to a sell offer affects
2759  // accepting that offer.
2760  {
2761  uint256 const offerMinterSellsToBuyer =
2762  keylet::nftoffer(minter, env.seq(minter)).key;
2763  env(token::createOffer(minter, nftokenID, drops(1)),
2764  token::destination(buyer),
2765  txflags(tfSellNFToken));
2766  env.close();
2767  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2768  BEAST_EXPECT(ownerCount(env, minter) == 2);
2769  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2770 
2771  // issuer cannot accept a sell offer where they are not the
2772  // destination.
2773  env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2774  ter(tecNO_PERMISSION));
2775  env.close();
2776  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2777  BEAST_EXPECT(ownerCount(env, minter) == 2);
2778  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2779 
2780  // However buyer can accept the sell offer.
2781  env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2782  env.close();
2783  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2784  BEAST_EXPECT(ownerCount(env, minter) == 0);
2785  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2786  }
2787 
2788  // Test how adding a Destination field to a buy offer affects
2789  // accepting that offer.
2790  {
2791  uint256 const offerMinterBuysFromBuyer =
2792  keylet::nftoffer(minter, env.seq(minter)).key;
2793  env(token::createOffer(minter, nftokenID, drops(1)),
2794  token::owner(buyer),
2795  token::destination(buyer));
2796  env.close();
2797  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2798  BEAST_EXPECT(ownerCount(env, minter) == 1);
2799  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2800 
2801  // issuer cannot accept a buy offer where they are the
2802  // destination.
2803  env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2804  ter(tecNO_PERMISSION));
2805  env.close();
2806  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2807  BEAST_EXPECT(ownerCount(env, minter) == 1);
2808  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2809 
2810  // Buyer accepts minter's offer.
2811  env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2812  env.close();
2813  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2814  BEAST_EXPECT(ownerCount(env, minter) == 1);
2815  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2816 
2817  // If a destination other than the NFToken owner is set, that
2818  // destination must act as a broker. The NFToken owner may not
2819  // simply accept the offer.
2820  uint256 const offerBuyerBuysFromMinter =
2821  keylet::nftoffer(buyer, env.seq(buyer)).key;
2822  env(token::createOffer(buyer, nftokenID, drops(1)),
2823  token::owner(minter),
2824  token::destination(broker));
2825  env.close();
2826  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2827  BEAST_EXPECT(ownerCount(env, minter) == 1);
2828  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2829 
2830  env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2831  ter(tecNO_PERMISSION));
2832  env.close();
2833 
2834  // Clean up the unused offer.
2835  env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2836  env.close();
2837  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2838  BEAST_EXPECT(ownerCount(env, minter) == 1);
2839  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2840  }
2841 
2842  // Show that a sell offer's Destination can broker that sell offer
2843  // to another account.
2844  {
2845  uint256 const offerMinterToBroker =
2846  keylet::nftoffer(minter, env.seq(minter)).key;
2847  env(token::createOffer(minter, nftokenID, drops(1)),
2848  token::destination(broker),
2849  txflags(tfSellNFToken));
2850 
2851  uint256 const offerBuyerToMinter =
2852  keylet::nftoffer(buyer, env.seq(buyer)).key;
2853  env(token::createOffer(buyer, nftokenID, drops(1)),
2854  token::owner(minter));
2855 
2856  env.close();
2857  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2858  BEAST_EXPECT(ownerCount(env, minter) == 2);
2859  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2860 
2861  // issuer cannot broker the offers, because they are not the
2862  // Destination.
2863  env(token::brokerOffers(
2864  issuer, offerBuyerToMinter, offerMinterToBroker),
2866  env.close();
2867  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2868  BEAST_EXPECT(ownerCount(env, minter) == 2);
2869  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2870 
2871  // Since broker is the sell offer's destination, they can broker
2872  // the two offers.
2873  env(token::brokerOffers(
2874  broker, offerBuyerToMinter, offerMinterToBroker));
2875  env.close();
2876  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2877  BEAST_EXPECT(ownerCount(env, minter) == 0);
2878  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2879  }
2880 
2881  // Show that brokered mode cannot complete a transfer where the
2882  // Destination doesn't match, but can complete if the Destination
2883  // does match.
2884  {
2885  uint256 const offerBuyerToMinter =
2886  keylet::nftoffer(buyer, env.seq(buyer)).key;
2887  env(token::createOffer(buyer, nftokenID, drops(1)),
2888  token::destination(minter),
2889  txflags(tfSellNFToken));
2890 
2891  uint256 const offerMinterToBuyer =
2892  keylet::nftoffer(minter, env.seq(minter)).key;
2893  env(token::createOffer(minter, nftokenID, drops(1)),
2894  token::owner(buyer));
2895 
2896  uint256 const offerIssuerToBuyer =
2897  keylet::nftoffer(issuer, env.seq(issuer)).key;
2898  env(token::createOffer(issuer, nftokenID, drops(1)),
2899  token::owner(buyer));
2900 
2901  env.close();
2902  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2903  BEAST_EXPECT(ownerCount(env, minter) == 1);
2904  BEAST_EXPECT(ownerCount(env, buyer) == 2);
2905 
2906  // Cannot broker offers when the sell destination is not the buyer.
2907  env(token::brokerOffers(
2908  broker, offerIssuerToBuyer, offerBuyerToMinter),
2910  env.close();
2911  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2912  BEAST_EXPECT(ownerCount(env, minter) == 1);
2913  BEAST_EXPECT(ownerCount(env, buyer) == 2);
2914 
2915  // Broker is successful when destination is buyer.
2916  env(token::brokerOffers(
2917  broker, offerMinterToBuyer, offerBuyerToMinter));
2918  env.close();
2919  BEAST_EXPECT(ownerCount(env, issuer) == 1);
2920  BEAST_EXPECT(ownerCount(env, minter) == 1);
2921  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2922 
2923  // Clean out the unconsumed offer.
2924  env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
2925  env.close();
2926  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2927  BEAST_EXPECT(ownerCount(env, minter) == 1);
2928  BEAST_EXPECT(ownerCount(env, buyer) == 0);
2929  }
2930 
2931  // Show that if a buy and a sell offer both have the same destination,
2932  // then that destination can broker the offers.
2933  {
2934  uint256 const offerMinterToBroker =
2935  keylet::nftoffer(minter, env.seq(minter)).key;
2936  env(token::createOffer(minter, nftokenID, drops(1)),
2937  token::destination(broker),
2938  txflags(tfSellNFToken));
2939 
2940  uint256 const offerBuyerToBroker =
2941  keylet::nftoffer(buyer, env.seq(buyer)).key;
2942  env(token::createOffer(buyer, nftokenID, drops(1)),
2943  token::owner(minter),
2944  token::destination(broker));
2945 
2946  // Cannot broker offers when the sell destination is not the buyer
2947  // or the broker.
2948  env(token::brokerOffers(
2949  issuer, offerBuyerToBroker, offerMinterToBroker),
2951  env.close();
2952  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2953  BEAST_EXPECT(ownerCount(env, minter) == 2);
2954  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2955 
2956  // Broker is successful if they are the destination of both offers.
2957  env(token::brokerOffers(
2958  broker, offerBuyerToBroker, offerMinterToBroker));
2959  env.close();
2960  BEAST_EXPECT(ownerCount(env, issuer) == 0);
2961  BEAST_EXPECT(ownerCount(env, minter) == 0);
2962  BEAST_EXPECT(ownerCount(env, buyer) == 1);
2963  }
2964  }
2965 
2966  void
2968  {
2969  // Explore the CreateOffer Expiration field.
2970  testcase("Create offer expiration");
2971 
2972  using namespace test::jtx;
2973 
2974  Env env{*this, features};
2975 
2976  Account const issuer{"issuer"};
2977  Account const minter{"minter"};
2978  Account const buyer{"buyer"};
2979 
2980  env.fund(XRP(1000), issuer, minter, buyer);
2981 
2982  // We want to explore how issuers vs minters fits into the permission
2983  // scheme. So issuer issues and minter mints.
2984  env(token::setMinter(issuer, minter));
2985  env.close();
2986 
2987  uint256 const nftokenID0 =
2988  token::getNextID(env, issuer, 0, tfTransferable);
2989  env(token::mint(minter, 0),
2990  token::issuer(issuer),
2991  txflags(tfTransferable));
2992  env.close();
2993 
2994  uint256 const nftokenID1 =
2995  token::getNextID(env, issuer, 0, tfTransferable);
2996  env(token::mint(minter, 0),
2997  token::issuer(issuer),
2998  txflags(tfTransferable));
2999  env.close();
3000 
3001  // Test how adding an Expiration field to an offer affects permissions
3002  // for cancelling offers.
3003  {
3004  std::uint32_t const expiration = lastClose(env) + 25;
3005 
3006  uint256 const offerMinterToIssuer =
3007  keylet::nftoffer(minter, env.seq(minter)).key;
3008  env(token::createOffer(minter, nftokenID0, drops(1)),
3009  token::destination(issuer),
3010  token::expiration(expiration),
3011  txflags(tfSellNFToken));
3012 
3013  uint256 const offerMinterToAnyone =
3014  keylet::nftoffer(minter, env.seq(minter)).key;
3015  env(token::createOffer(minter, nftokenID0, drops(1)),
3016  token::expiration(expiration),
3017  txflags(tfSellNFToken));
3018 
3019  uint256 const offerIssuerToMinter =
3020  keylet::nftoffer(issuer, env.seq(issuer)).key;
3021  env(token::createOffer(issuer, nftokenID0, drops(1)),
3022  token::owner(minter),
3023  token::expiration(expiration));
3024 
3025  uint256 const offerBuyerToMinter =
3026  keylet::nftoffer(buyer, env.seq(buyer)).key;
3027  env(token::createOffer(buyer, nftokenID0, drops(1)),
3028  token::owner(minter),
3029  token::expiration(expiration));
3030  env.close();
3031  BEAST_EXPECT(ownerCount(env, issuer) == 1);
3032  BEAST_EXPECT(ownerCount(env, minter) == 3);
3033  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3034 
3035  // Test who gets to cancel the offers. Anyone outside of the
3036  // offer-owner/destination pair should not be able to cancel
3037  // unexpired offers.
3038  //
3039  // Note that these are tec responses, so these transactions will
3040  // not be retried by the ledger.
3041  env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3042  ter(tecNO_PERMISSION));
3043  env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3044  ter(tecNO_PERMISSION));
3045  env.close();
3046  BEAST_EXPECT(lastClose(env) < expiration);
3047  BEAST_EXPECT(ownerCount(env, issuer) == 1);
3048  BEAST_EXPECT(ownerCount(env, minter) == 3);
3049  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3050 
3051  // The offer creator can cancel their own unexpired offer.
3052  env(token::cancelOffer(minter, {offerMinterToAnyone}));
3053 
3054  // The destination of a sell offer can cancel the NFT owner's
3055  // unexpired offer.
3056  env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3057 
3058  // Close enough ledgers to get past the expiration.
3059  while (lastClose(env) < expiration)
3060  env.close();
3061 
3062  BEAST_EXPECT(ownerCount(env, issuer) == 1);
3063  BEAST_EXPECT(ownerCount(env, minter) == 1);
3064  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3065 
3066  // Anyone can cancel expired offers.
3067  env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3068  env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3069  env.close();
3070  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3071  BEAST_EXPECT(ownerCount(env, minter) == 1);
3072  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3073  }
3074  // Show that:
3075  // 1. An unexpired sell offer with an expiration can be accepted.
3076  // 2. An expired sell offer cannot be accepted and remains
3077  // in ledger after the accept fails.
3078  {
3079  std::uint32_t const expiration = lastClose(env) + 25;
3080 
3081  uint256 const offer0 =
3082  keylet::nftoffer(minter, env.seq(minter)).key;
3083  env(token::createOffer(minter, nftokenID0, drops(1)),
3084  token::expiration(expiration),
3085  txflags(tfSellNFToken));
3086 
3087  uint256 const offer1 =
3088  keylet::nftoffer(minter, env.seq(minter)).key;
3089  env(token::createOffer(minter, nftokenID1, drops(1)),
3090  token::expiration(expiration),
3091  txflags(tfSellNFToken));
3092  env.close();
3093  BEAST_EXPECT(lastClose(env) < expiration);
3094  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3095  BEAST_EXPECT(ownerCount(env, minter) == 3);
3096  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3097 
3098  // Anyone can accept an unexpired sell offer.
3099  env(token::acceptSellOffer(buyer, offer0));
3100 
3101  // Close enough ledgers to get past the expiration.
3102  while (lastClose(env) < expiration)
3103  env.close();
3104 
3105  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3106  BEAST_EXPECT(ownerCount(env, minter) == 2);
3107  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3108 
3109  // No one can accept an expired sell offer.
3110  env(token::acceptSellOffer(buyer, offer1), ter(tecEXPIRED));
3111  env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
3112  env.close();
3113 
3114  // The expired sell offer is still in the ledger.
3115  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3116  BEAST_EXPECT(ownerCount(env, minter) == 2);
3117  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3118 
3119  // Anyone can cancel the expired sell offer.
3120  env(token::cancelOffer(issuer, {offer1}));
3121  env.close();
3122  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3123  BEAST_EXPECT(ownerCount(env, minter) == 1);
3124  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3125 
3126  // Transfer nftokenID0 back to minter so we start the next test in
3127  // a simple place.
3128  uint256 const offerSellBack =
3129  keylet::nftoffer(buyer, env.seq(buyer)).key;
3130  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3131  txflags(tfSellNFToken),
3132  token::destination(minter));
3133  env.close();
3134  env(token::acceptSellOffer(minter, offerSellBack));
3135  env.close();
3136  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3137  BEAST_EXPECT(ownerCount(env, minter) == 1);
3138  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3139  }
3140  // Show that:
3141  // 1. An unexpired buy offer with an expiration can be accepted.
3142  // 2. An expired buy offer cannot be accepted and remains
3143  // in ledger after the accept fails.
3144  {
3145  std::uint32_t const expiration = lastClose(env) + 25;
3146 
3147  uint256 const offer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3148  env(token::createOffer(buyer, nftokenID0, drops(1)),
3149  token::owner(minter),
3150  token::expiration(expiration));
3151 
3152  uint256 const offer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
3153  env(token::createOffer(buyer, nftokenID1, drops(1)),
3154  token::owner(minter),
3155  token::expiration(expiration));
3156  env.close();
3157  BEAST_EXPECT(lastClose(env) < expiration);
3158  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3159  BEAST_EXPECT(ownerCount(env, minter) == 1);
3160  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3161 
3162  // An unexpired buy offer can be accepted.
3163  env(token::acceptBuyOffer(minter, offer0));
3164 
3165  // Close enough ledgers to get past the expiration.
3166  while (lastClose(env) < expiration)
3167  env.close();
3168 
3169  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3170  BEAST_EXPECT(ownerCount(env, minter) == 1);
3171  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3172 
3173  // An expired buy offer cannot be accepted.
3174  env(token::acceptBuyOffer(minter, offer1), ter(tecEXPIRED));
3175  env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
3176  env.close();
3177 
3178  // The expired buy offer is still in the ledger.
3179  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3180  BEAST_EXPECT(ownerCount(env, minter) == 1);
3181  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3182 
3183  // Anyone can cancel the expired buy offer.
3184  env(token::cancelOffer(issuer, {offer1}));
3185  env.close();
3186  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3187  BEAST_EXPECT(ownerCount(env, minter) == 1);
3188  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3189 
3190  // Transfer nftokenID0 back to minter so we start the next test in
3191  // a simple place.
3192  uint256 const offerSellBack =
3193  keylet::nftoffer(buyer, env.seq(buyer)).key;
3194  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3195  txflags(tfSellNFToken),
3196  token::destination(minter));
3197  env.close();
3198  env(token::acceptSellOffer(minter, offerSellBack));
3199  env.close();
3200  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3201  BEAST_EXPECT(ownerCount(env, minter) == 1);
3202  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3203  }
3204  // Show that in brokered mode:
3205  // 1. An unexpired sell offer with an expiration can be accepted.
3206  // 2. An expired sell offer cannot be accepted and remains
3207  // in ledger after the accept fails.
3208  {
3209  std::uint32_t const expiration = lastClose(env) + 25;
3210 
3211  uint256 const sellOffer0 =
3212  keylet::nftoffer(minter, env.seq(minter)).key;
3213  env(token::createOffer(minter, nftokenID0, drops(1)),
3214  token::expiration(expiration),
3215  txflags(tfSellNFToken));
3216 
3217  uint256 const sellOffer1 =
3218  keylet::nftoffer(minter, env.seq(minter)).key;
3219  env(token::createOffer(minter, nftokenID1, drops(1)),
3220  token::expiration(expiration),
3221  txflags(tfSellNFToken));
3222 
3223  uint256 const buyOffer0 =
3224  keylet::nftoffer(buyer, env.seq(buyer)).key;
3225  env(token::createOffer(buyer, nftokenID0, drops(1)),
3226  token::owner(minter));
3227 
3228  uint256 const buyOffer1 =
3229  keylet::nftoffer(buyer, env.seq(buyer)).key;
3230  env(token::createOffer(buyer, nftokenID1, drops(1)),
3231  token::owner(minter));
3232 
3233  env.close();
3234  BEAST_EXPECT(lastClose(env) < expiration);
3235  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3236  BEAST_EXPECT(ownerCount(env, minter) == 3);
3237  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3238 
3239  // An unexpired offer can be brokered.
3240  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3241 
3242  // Close enough ledgers to get past the expiration.
3243  while (lastClose(env) < expiration)
3244  env.close();
3245 
3246  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3247  BEAST_EXPECT(ownerCount(env, minter) == 2);
3248  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3249 
3250  // If the sell offer is expired it cannot be brokered.
3251  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3252  ter(tecEXPIRED));
3253  env.close();
3254 
3255  // The expired sell offer is still in the ledger.
3256  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3257  BEAST_EXPECT(ownerCount(env, minter) == 2);
3258  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3259 
3260  // Anyone can cancel the expired sell offer.
3261  env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3262  env.close();
3263  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3264  BEAST_EXPECT(ownerCount(env, minter) == 1);
3265  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3266 
3267  // Transfer nftokenID0 back to minter so we start the next test in
3268  // a simple place.
3269  uint256 const offerSellBack =
3270  keylet::nftoffer(buyer, env.seq(buyer)).key;
3271  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3272  txflags(tfSellNFToken),
3273  token::destination(minter));
3274  env.close();
3275  env(token::acceptSellOffer(minter, offerSellBack));
3276  env.close();
3277  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3278  BEAST_EXPECT(ownerCount(env, minter) == 1);
3279  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3280  }
3281  // Show that in brokered mode:
3282  // 1. An unexpired buy offer with an expiration can be accepted.
3283  // 2. An expired buy offer cannot be accepted and remains
3284  // in ledger after the accept fails.
3285  {
3286  std::uint32_t const expiration = lastClose(env) + 25;
3287 
3288  uint256 const sellOffer0 =
3289  keylet::nftoffer(minter, env.seq(minter)).key;
3290  env(token::createOffer(minter, nftokenID0, drops(1)),
3291  txflags(tfSellNFToken));
3292 
3293  uint256 const sellOffer1 =
3294  keylet::nftoffer(minter, env.seq(minter)).key;
3295  env(token::createOffer(minter, nftokenID1, drops(1)),
3296  txflags(tfSellNFToken));
3297 
3298  uint256 const buyOffer0 =
3299  keylet::nftoffer(buyer, env.seq(buyer)).key;
3300  env(token::createOffer(buyer, nftokenID0, drops(1)),
3301  token::expiration(expiration),
3302  token::owner(minter));
3303 
3304  uint256 const buyOffer1 =
3305  keylet::nftoffer(buyer, env.seq(buyer)).key;
3306  env(token::createOffer(buyer, nftokenID1, drops(1)),
3307  token::expiration(expiration),
3308  token::owner(minter));
3309 
3310  env.close();
3311  BEAST_EXPECT(lastClose(env) < expiration);
3312  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3313  BEAST_EXPECT(ownerCount(env, minter) == 3);
3314  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3315 
3316  // An unexpired offer can be brokered.
3317  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3318 
3319  // Close enough ledgers to get past the expiration.
3320  while (lastClose(env) < expiration)
3321  env.close();
3322 
3323  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3324  BEAST_EXPECT(ownerCount(env, minter) == 2);
3325  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3326 
3327  // If the buy offer is expired it cannot be brokered.
3328  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3329  ter(tecEXPIRED));
3330  env.close();
3331 
3332  // The expired buy offer is still in the ledger.
3333  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3334  BEAST_EXPECT(ownerCount(env, minter) == 2);
3335  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3336 
3337  // Anyone can cancel the expired buy offer.
3338  env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3339  env.close();
3340  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3341  BEAST_EXPECT(ownerCount(env, minter) == 1);
3342  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3343 
3344  // Transfer nftokenID0 back to minter so we start the next test in
3345  // a simple place.
3346  uint256 const offerSellBack =
3347  keylet::nftoffer(buyer, env.seq(buyer)).key;
3348  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3349  txflags(tfSellNFToken),
3350  token::destination(minter));
3351  env.close();
3352  env(token::acceptSellOffer(minter, offerSellBack));
3353  env.close();
3354  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3355  BEAST_EXPECT(ownerCount(env, minter) == 1);
3356  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3357  }
3358  // Show that in brokered mode:
3359  // 1. An unexpired buy/sell offer pair with an expiration can be
3360  // accepted.
3361  // 2. An expired buy/sell offer pair cannot be accepted and they
3362  // remain in ledger after the accept fails.
3363  {
3364  std::uint32_t const expiration = lastClose(env) + 25;
3365 
3366  uint256 const sellOffer0 =
3367  keylet::nftoffer(minter, env.seq(minter)).key;
3368  env(token::createOffer(minter, nftokenID0, drops(1)),
3369  token::expiration(expiration),
3370  txflags(tfSellNFToken));
3371 
3372  uint256 const sellOffer1 =
3373  keylet::nftoffer(minter, env.seq(minter)).key;
3374  env(token::createOffer(minter, nftokenID1, drops(1)),
3375  token::expiration(expiration),
3376  txflags(tfSellNFToken));
3377 
3378  uint256 const buyOffer0 =
3379  keylet::nftoffer(buyer, env.seq(buyer)).key;
3380  env(token::createOffer(buyer, nftokenID0, drops(1)),
3381  token::expiration(expiration),
3382  token::owner(minter));
3383 
3384  uint256 const buyOffer1 =
3385  keylet::nftoffer(buyer, env.seq(buyer)).key;
3386  env(token::createOffer(buyer, nftokenID1, drops(1)),
3387  token::expiration(expiration),
3388  token::owner(minter));
3389 
3390  env.close();
3391  BEAST_EXPECT(lastClose(env) < expiration);
3392  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3393  BEAST_EXPECT(ownerCount(env, minter) == 3);
3394  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3395 
3396  // Unexpired offers can be brokered.
3397  env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3398 
3399  // Close enough ledgers to get past the expiration.
3400  while (lastClose(env) < expiration)
3401  env.close();
3402 
3403  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3404  BEAST_EXPECT(ownerCount(env, minter) == 2);
3405  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3406 
3407  // If the offers are expired they cannot be brokered.
3408  env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3409  ter(tecEXPIRED));
3410  env.close();
3411 
3412  // The expired offers are still in the ledger.
3413  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3414  BEAST_EXPECT(ownerCount(env, minter) == 2);
3415  BEAST_EXPECT(ownerCount(env, buyer) == 2);
3416 
3417  // Anyone can cancel the expired offers.
3418  env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3419  env.close();
3420  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3421  BEAST_EXPECT(ownerCount(env, minter) == 1);
3422  BEAST_EXPECT(ownerCount(env, buyer) == 1);
3423 
3424  // Transfer nftokenID0 back to minter so we start the next test in
3425  // a simple place.
3426  uint256 const offerSellBack =
3427  keylet::nftoffer(buyer, env.seq(buyer)).key;
3428  env(token::createOffer(buyer, nftokenID0, XRP(0)),
3429  txflags(tfSellNFToken),
3430  token::destination(minter));
3431  env.close();
3432  env(token::acceptSellOffer(minter, offerSellBack));
3433  env.close();
3434  BEAST_EXPECT(ownerCount(env, issuer) == 0);
3435  BEAST_EXPECT(ownerCount(env, minter) == 1);
3436  BEAST_EXPECT(ownerCount(env, buyer) == 0);
3437  }
3438  }
3439 
3440  void
3442  {
3443  // Look at offer canceling.
3444  testcase("Cancel offers");
3445 
3446  using namespace test::jtx;
3447 
3448  Env env{*this, features};
3449 
3450  Account const alice("alice");
3451  Account const becky("becky");
3452  Account const minter("minter");
3453  env.fund(XRP(50000), alice, becky, minter);
3454  env.close();
3455 
3456  // alice has a minter to see if minters have offer canceling permission.
3457  env(token::setMinter(alice, minter));
3458  env.close();
3459 
3460  uint256 const nftokenID =
3461  token::getNextID(env, alice, 0, tfTransferable);
3462  env(token::mint(alice, 0), txflags(tfTransferable));
3463  env.close();
3464 
3465  // Anyone can cancel an expired offer.
3466  uint256 const expiredOfferIndex =
3467  keylet::nftoffer(alice, env.seq(alice)).key;
3468 
3469  env(token::createOffer(alice, nftokenID, XRP(1000)),
3470  txflags(tfSellNFToken),
3471  token::expiration(lastClose(env) + 13));
3472  env.close();
3473 
3474  // The offer has not expired yet, so becky can't cancel it now.
3475  BEAST_EXPECT(ownerCount(env, alice) == 2);
3476  env(token::cancelOffer(becky, {expiredOfferIndex}),
3477  ter(tecNO_PERMISSION));
3478  env.close();
3479 
3480  // Close a couple of ledgers and advance the time. Then becky
3481  // should be able to cancel the (now) expired offer.
3482  env.close();
3483  env.close();
3484  env(token::cancelOffer(becky, {expiredOfferIndex}));
3485  env.close();
3486  BEAST_EXPECT(ownerCount(env, alice) == 1);
3487 
3488  // Create a couple of offers with a destination. Those offers
3489  // should be cancellable by the creator and the destination.
3490  uint256 const dest1OfferIndex =
3491  keylet::nftoffer(alice, env.seq(alice)).key;
3492 
3493  env(token::createOffer(alice, nftokenID, XRP(1000)),
3494  token::destination(becky),
3495  txflags(tfSellNFToken));
3496  env.close();
3497  BEAST_EXPECT(ownerCount(env, alice) == 2);
3498 
3499  // Minter can't cancel that offer, but becky (the destination) can.
3500  env(token::cancelOffer(minter, {dest1OfferIndex}),
3501  ter(tecNO_PERMISSION));
3502  env.close();
3503  BEAST_EXPECT(ownerCount(env, alice) == 2);
3504 
3505  env(token::cancelOffer(becky, {dest1OfferIndex}));
3506  env.close();
3507  BEAST_EXPECT(ownerCount(env, alice) == 1);
3508 
3509  // alice can cancel her own offer, even if becky is the destination.
3510  uint256 const dest2OfferIndex =
3511  keylet::nftoffer(alice, env.seq(alice)).key;
3512 
3513  env(token::createOffer(alice, nftokenID, XRP(1000)),
3514  token::destination(becky),
3515  txflags(tfSellNFToken));
3516  env.close();
3517  BEAST_EXPECT(ownerCount(env, alice) == 2);
3518 
3519  env(token::cancelOffer(alice, {dest2OfferIndex}));
3520  env.close();
3521  BEAST_EXPECT(ownerCount(env, alice) == 1);
3522 
3523  // The issuer has no special permissions regarding offer cancellation.
3524  // Minter creates a token with alice as issuer. alice cannot cancel
3525  // minter's offer.
3526  uint256 const mintersNFTokenID =
3527  token::getNextID(env, alice, 0, tfTransferable);
3528  env(token::mint(minter, 0),
3529  token::issuer(alice),
3530  txflags(tfTransferable));
3531  env.close();
3532 
3533  uint256 const minterOfferIndex =
3534  keylet::nftoffer(minter, env.seq(minter)).key;
3535 
3536  env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3537  txflags(tfSellNFToken));
3538  env.close();
3539  BEAST_EXPECT(ownerCount(env, minter) == 2);
3540 
3541  // Nobody other than minter should be able to cancel minter's offer.
3542  env(token::cancelOffer(alice, {minterOfferIndex}),
3543  ter(tecNO_PERMISSION));
3544  env(token::cancelOffer(becky, {minterOfferIndex}),
3545  ter(tecNO_PERMISSION));
3546  env.close();
3547  BEAST_EXPECT(ownerCount(env, minter) == 2);
3548 
3549  env(token::cancelOffer(minter, {minterOfferIndex}));
3550  env.close();
3551  BEAST_EXPECT(ownerCount(env, minter) == 1);
3552  }
3553 
3554  void
3556  {
3557  // Look at the case where too many offers are passed in a cancel.
3558  testcase("Cancel too many offers");
3559 
3560  using namespace test::jtx;
3561 
3562  Env env{*this, features};
3563 
3564  // We want to maximize the metadata from a cancel offer transaction to
3565  // make sure we don't hit metadata limits. The way we'll do that is:
3566  //
3567  // 1. Generate twice as many separate funded accounts as we have
3568  // offers.
3569  // 2.
3570  // a. One of these accounts mints an NFT with a full URL.
3571  // b. The other account makes an offer that will expire soon.
3572  // 3. After all of these offers have expired, cancel all of the
3573  // expired offers in a single transaction.
3574  //
3575  // I can't think of any way to increase the metadata beyond this,
3576  // but I'm open to ideas.
3577  Account const alice("alice");
3578  env.fund(XRP(1000), alice);
3579  env.close();
3580 
3581  std::string const uri(maxTokenURILength, '?');
3582  std::vector<uint256> offerIndexes;
3583  offerIndexes.reserve(maxTokenOfferCancelCount + 1);
3584  for (uint32_t i = 0; i < maxTokenOfferCancelCount + 1; ++i)
3585  {
3586  Account const nftAcct(std::string("nftAcct") + std::to_string(i));
3587  Account const offerAcct(
3588  std::string("offerAcct") + std::to_string(i));
3589  env.fund(XRP(1000), nftAcct, offerAcct);
3590  env.close();
3591 
3592  uint256 const nftokenID =
3593  token::getNextID(env, nftAcct, 0, tfTransferable);
3594  env(token::mint(nftAcct, 0),
3595  token::uri(uri),
3596  txflags(tfTransferable));
3597  env.close();
3598 
3599  offerIndexes.push_back(
3600  keylet::nftoffer(offerAcct, env.seq(offerAcct)).key);
3601  env(token::createOffer(offerAcct, nftokenID, drops(1)),
3602  token::owner(nftAcct),
3603  token::expiration(lastClose(env) + 5));
3604  env.close();
3605  }
3606 
3607  // Close the ledger so the last of the offers expire.
3608  env.close();
3609 
3610  // All offers should be in the ledger.
3611  for (uint256 const& offerIndex : offerIndexes)
3612  {
3613  BEAST_EXPECT(env.le(keylet::nftoffer(offerIndex)));
3614  }
3615 
3616  // alice attempts to cancel all of the expired offers. There is one
3617  // too many so the request fails.
3618  env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3619  env.close();
3620 
3621  // However alice can cancel just one of the offers.
3622  env(token::cancelOffer(alice, {offerIndexes.back()}));
3623  env.close();
3624 
3625  // Verify that offer is gone from the ledger.
3626  BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndexes.back())));
3627  offerIndexes.pop_back();
3628 
3629  // But alice adds a sell offer to the list...
3630  {
3631  uint256 const nftokenID =
3632  token::getNextID(env, alice, 0, tfTransferable);
3633  env(token::mint(alice, 0),
3634  token::uri(uri),
3635  txflags(tfTransferable));
3636  env.close();
3637 
3638  offerIndexes.push_back(keylet::nftoffer(alice, env.seq(alice)).key);
3639  env(token::createOffer(alice, nftokenID, drops(1)),
3640  txflags(tfSellNFToken));
3641  env.close();
3642 
3643  // alice's owner count should now to 2 for the nft and the offer.
3644  BEAST_EXPECT(ownerCount(env, alice) == 2);
3645 
3646  // Because alice added the sell offer there are still too many
3647  // offers in the list to cancel.
3648  env(token::cancelOffer(alice, offerIndexes), ter(temMALFORMED));
3649  env.close();
3650 
3651  // alice burns her nft which removes the nft and the offer.
3652  env(token::burn(alice, nftokenID));
3653  env.close();
3654 
3655  // If alice's owner count is zero we can see that the offer
3656  // and nft are both gone.
3657  BEAST_EXPECT(ownerCount(env, alice) == 0);
3658  offerIndexes.pop_back();
3659  }
3660 
3661  // Now there are few enough offers in the list that they can all
3662  // be cancelled in a single transaction.
3663  env(token::cancelOffer(alice, offerIndexes));
3664  env.close();
3665 
3666  // Verify that remaining offers are gone from the ledger.
3667  for (uint256 const& offerIndex : offerIndexes)
3668  {
3669  BEAST_EXPECT(!env.le(keylet::nftoffer(offerIndex)));
3670  }
3671  }
3672 
3673  void
3675  {
3676  // Look at the case where too many offers are passed in a cancel.
3677  testcase("Brokered NFT offer accept");
3678 
3679  using namespace test::jtx;
3680 
3681  Env env{*this, features};
3682 
3683  // The most important thing to explore here is the way funds are
3684  // assigned from the buyer to...
3685  // o the Seller,
3686  // o the Broker, and
3687  // o the Issuer (in the case of a transfer fee).
3688 
3689  Account const issuer{"issuer"};
3690  Account const minter{"minter"};
3691  Account const buyer{"buyer"};
3692  Account const broker{"broker"};
3693  Account const gw{"gw"};
3694  IOU const gwXAU(gw["XAU"]);
3695 
3696  env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3697  env.close();
3698 
3699  env(trust(issuer, gwXAU(2000)));
3700  env(trust(minter, gwXAU(2000)));
3701  env(trust(buyer, gwXAU(2000)));
3702  env(trust(broker, gwXAU(2000)));
3703  env.close();
3704 
3705  env(token::setMinter(issuer, minter));
3706  env.close();
3707 
3708  // Lambda to check owner count of all accounts is one.
3709  auto checkOwnerCountIsOne =
3710  [this, &env](
3712  accounts,
3713  int line) {
3714  for (Account const& acct : accounts)
3715  {
3716  if (std::uint32_t ownerCount = this->ownerCount(env, acct);
3717  ownerCount != 1)
3718  {
3719  std::stringstream ss;
3720  ss << "Account " << acct.human()
3721  << " expected ownerCount == 1. Got " << ownerCount;
3722  fail(ss.str(), __FILE__, line);
3723  }
3724  }
3725  };
3726 
3727  // Lambda that mints an NFT and returns the nftID.
3728  auto mintNFT = [&env, &issuer, &minter](std::uint16_t xferFee = 0) {
3729  uint256 const nftID =
3730  token::getNextID(env, issuer, 0, tfTransferable, xferFee);
3731  env(token::mint(minter, 0),
3732  token::issuer(issuer),
3733  token::xferFee(xferFee),
3734  txflags(tfTransferable));
3735  env.close();
3736  return nftID;
3737  };
3738 
3739  // o Seller is selling for zero XRP.
3740  // o Broker charges no fee.
3741  // o No transfer fee.
3742  //
3743  // Since minter is selling for zero the currency must be XRP.
3744  {
3745  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3746 
3747  uint256 const nftID = mintNFT();
3748 
3749  // minter creates their offer.
3750  uint256 const minterOfferIndex =
3751  keylet::nftoffer(minter, env.seq(minter)).key;
3752  env(token::createOffer(minter, nftID, XRP(0)),
3753  txflags(tfSellNFToken));
3754  env.close();
3755 
3756  // buyer creates their offer. Note: a buy offer can never
3757  // offer zero.
3758  uint256 const buyOfferIndex =
3759  keylet::nftoffer(buyer, env.seq(buyer)).key;
3760  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3761  env.close();
3762 
3763  auto const minterBalance = env.balance(minter);
3764  auto const buyerBalance = env.balance(buyer);
3765  auto const brokerBalance = env.balance(broker);
3766  auto const issuerBalance = env.balance(issuer);
3767 
3768  // Broker charges no brokerFee.
3769  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3770  env.close();
3771 
3772  // Note that minter's XRP balance goes up even though they
3773  // requested XRP(0).
3774  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3775  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3776  BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3777  BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3778 
3779  // Burn the NFT so the next test starts with a clean state.
3780  env(token::burn(buyer, nftID));
3781  env.close();
3782  }
3783 
3784  // o Seller is selling for zero XRP.
3785  // o Broker charges a fee.
3786  // o No transfer fee.
3787  //
3788  // Since minter is selling for zero the currency must be XRP.
3789  {
3790  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3791 
3792  uint256 const nftID = mintNFT();
3793 
3794  // minter creates their offer.
3795  uint256 const minterOfferIndex =
3796  keylet::nftoffer(minter, env.seq(minter)).key;
3797  env(token::createOffer(minter, nftID, XRP(0)),
3798  txflags(tfSellNFToken));
3799  env.close();
3800 
3801  // buyer creates their offer. Note: a buy offer can never
3802  // offer zero.
3803  uint256 const buyOfferIndex =
3804  keylet::nftoffer(buyer, env.seq(buyer)).key;
3805  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3806  env.close();
3807 
3808  // Broker attempts to charge a 1.1 XRP brokerFee and fails.
3809  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3810  token::brokerFee(XRP(1.1)),
3812  env.close();
3813 
3814  auto const minterBalance = env.balance(minter);
3815  auto const buyerBalance = env.balance(buyer);
3816  auto const brokerBalance = env.balance(broker);
3817  auto const issuerBalance = env.balance(issuer);
3818 
3819  // Broker charges a 0.5 XRP brokerFee.
3820  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3821  token::brokerFee(XRP(0.5)));
3822  env.close();
3823 
3824  // Note that minter's XRP balance goes up even though they
3825  // requested XRP(0).
3826  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3827  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3828  BEAST_EXPECT(
3829  env.balance(broker) == brokerBalance + XRP(0.5) - drops(10));
3830  BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3831 
3832  // Burn the NFT so the next test starts with a clean state.
3833  env(token::burn(buyer, nftID));
3834  env.close();
3835  }
3836 
3837  // o Seller is selling for zero XRP.
3838  // o Broker charges no fee.
3839  // o 50% transfer fee.
3840  //
3841  // Since minter is selling for zero the currency must be XRP.
3842  {
3843  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3844 
3845  uint256 const nftID = mintNFT(maxTransferFee);
3846 
3847  // minter creates their offer.
3848  uint256 const minterOfferIndex =
3849  keylet::nftoffer(minter, env.seq(minter)).key;
3850  env(token::createOffer(minter, nftID, XRP(0)),
3851  txflags(tfSellNFToken));
3852  env.close();
3853 
3854  // buyer creates their offer. Note: a buy offer can never
3855  // offer zero.
3856  uint256 const buyOfferIndex =
3857  keylet::nftoffer(buyer, env.seq(buyer)).key;
3858  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3859  env.close();
3860 
3861  auto const minterBalance = env.balance(minter);
3862  auto const buyerBalance = env.balance(buyer);
3863  auto const brokerBalance = env.balance(broker);
3864  auto const issuerBalance = env.balance(issuer);
3865 
3866  // Broker charges no brokerFee.
3867  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3868  env.close();
3869 
3870  // Note that minter's XRP balance goes up even though they
3871  // requested XRP(0).
3872  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3873  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3874  BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3875  BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
3876 
3877  // Burn the NFT so the next test starts with a clean state.
3878  env(token::burn(buyer, nftID));
3879  env.close();
3880  }
3881 
3882  // o Seller is selling for zero XRP.
3883  // o Broker charges 0.5 XRP.
3884  // o 50% transfer fee.
3885  //
3886  // Since minter is selling for zero the currency must be XRP.
3887  {
3888  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3889 
3890  uint256 const nftID = mintNFT(maxTransferFee);
3891 
3892  // minter creates their offer.
3893  uint256 const minterOfferIndex =
3894  keylet::nftoffer(minter, env.seq(minter)).key;
3895  env(token::createOffer(minter, nftID, XRP(0)),
3896  txflags(tfSellNFToken));
3897  env.close();
3898 
3899  // buyer creates their offer. Note: a buy offer can never
3900  // offer zero.
3901  uint256 const buyOfferIndex =
3902  keylet::nftoffer(buyer, env.seq(buyer)).key;
3903  env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3904  env.close();
3905 
3906  auto const minterBalance = env.balance(minter);
3907  auto const buyerBalance = env.balance(buyer);
3908  auto const brokerBalance = env.balance(broker);
3909  auto const issuerBalance = env.balance(issuer);
3910 
3911  // Broker charges a 0.75 XRP brokerFee.
3912  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3913  token::brokerFee(XRP(0.75)));
3914  env.close();
3915 
3916  // Note that, with a 50% transfer fee, issuer gets 1/2 of what's
3917  // left _after_ broker takes their fee. minter gets the remainder
3918  // after both broker and minter take their cuts
3919  BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
3920  BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3921  BEAST_EXPECT(
3922  env.balance(broker) == brokerBalance + XRP(0.75) - drops(10));
3923  BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
3924 
3925  // Burn the NFT so the next test starts with a clean state.
3926  env(token::burn(buyer, nftID));
3927  env.close();
3928  }
3929 
3930  // Lambda to set the balance of all passed in accounts to gwXAU(1000).
3931  auto setXAUBalance_1000 =
3932  [this, &gw, &gwXAU, &env](
3934  accounts,
3935  int line) {
3936  for (Account const& acct : accounts)
3937  {
3938  static const auto xau1000 = gwXAU(1000);
3939  auto const balance = env.balance(acct, gwXAU);
3940  if (balance < xau1000)
3941  {
3942  env(pay(gw, acct, xau1000 - balance));
3943  env.close();
3944  }
3945  else if (balance > xau1000)
3946  {
3947  env(pay(acct, gw, balance - xau1000));
3948  env.close();
3949  }
3950  if (env.balance(acct, gwXAU) != xau1000)
3951  {
3952  std::stringstream ss;
3953  ss << "Unable to set " << acct.human()
3954  << " account balance to gwXAU(1000)";
3955  this->fail(ss.str(), __FILE__, line);
3956  }
3957  }
3958  };
3959 
3960  // The buyer and seller have identical amounts and there is no
3961  // transfer fee.
3962  {
3963  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3964  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
3965 
3966  uint256 const nftID = mintNFT();
3967 
3968  // minter creates their offer.
3969  uint256 const minterOfferIndex =
3970  keylet::nftoffer(minter, env.seq(minter)).key;
3971  env(token::createOffer(minter, nftID, gwXAU(1000)),
3972  txflags(tfSellNFToken));
3973  env.close();
3974 
3975  {
3976  // buyer creates an offer for more XAU than they currently own.
3977  uint256 const buyOfferIndex =
3978  keylet::nftoffer(buyer, env.seq(buyer)).key;
3979  env(token::createOffer(buyer, nftID, gwXAU(1001)),
3980  token::owner(minter));
3981  env.close();
3982 
3983  // broker attempts to broker the offers but cannot.
3984  env(token::brokerOffers(
3985  broker, buyOfferIndex, minterOfferIndex),
3986  ter(tecINSUFFICIENT_FUNDS));
3987  env.close();
3988 
3989  // Cancel buyer's bad offer so the next test starts in a
3990  // clean state.
3991  env(token::cancelOffer(buyer, {buyOfferIndex}));
3992  env.close();
3993  }
3994  {
3995  // buyer creates an offer for less that what minter is asking.
3996  uint256 const buyOfferIndex =
3997  keylet::nftoffer(buyer, env.seq(buyer)).key;
3998  env(token::createOffer(buyer, nftID, gwXAU(999)),
3999  token::owner(minter));
4000  env.close();
4001 
4002  // broker attempts to broker the offers but cannot.
4003  env(token::brokerOffers(
4004  broker, buyOfferIndex, minterOfferIndex),
4006  env.close();
4007 
4008  // Cancel buyer's bad offer so the next test starts in a
4009  // clean state.
4010  env(token::cancelOffer(buyer, {buyOfferIndex}));
4011  env.close();
4012  }
4013 
4014  // buyer creates a large enough offer.
4015  uint256 const buyOfferIndex =
4016  keylet::nftoffer(buyer, env.seq(buyer)).key;
4017  env(token::createOffer(buyer, nftID, gwXAU(1000)),
4018  token::owner(minter));
4019  env.close();
4020 
4021  // Broker attempts to charge a brokerFee but cannot.
4022  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4023  token::brokerFee(gwXAU(0.1)),
4025  env.close();
4026 
4027  // broker charges no brokerFee and succeeds.
4028  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
4029  env.close();
4030 
4031  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4032  BEAST_EXPECT(ownerCount(env, minter) == 1);
4033  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4034  BEAST_EXPECT(ownerCount(env, broker) == 1);
4035  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4036  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4037  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4038  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4039 
4040  // Burn the NFT so the next test starts with a clean state.
4041  env(token::burn(buyer, nftID));
4042  env.close();
4043  }
4044 
4045  // seller offers more than buyer is asking.
4046  // There are both transfer and broker fees.
4047  {
4048  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4049  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4050 
4051  uint256 const nftID = mintNFT(maxTransferFee);
4052 
4053  // minter creates their offer.
4054  uint256 const minterOfferIndex =
4055  keylet::nftoffer(minter, env.seq(minter)).key;
4056  env(token::createOffer(minter, nftID, gwXAU(900)),
4057  txflags(tfSellNFToken));
4058  env.close();
4059  {
4060  // buyer creates an offer for more XAU than they currently own.
4061  uint256 const buyOfferIndex =
4062  keylet::nftoffer(buyer, env.seq(buyer)).key;
4063  env(token::createOffer(buyer, nftID, gwXAU(1001)),
4064  token::owner(minter));
4065  env.close();
4066 
4067  // broker attempts to broker the offers but cannot.
4068  env(token::brokerOffers(
4069  broker, buyOfferIndex, minterOfferIndex),
4070  ter(tecINSUFFICIENT_FUNDS));
4071  env.close();
4072 
4073  // Cancel buyer's bad offer so the next test starts in a
4074  // clean state.
4075  env(token::cancelOffer(buyer, {buyOfferIndex}));
4076  env.close();
4077  }
4078  {
4079  // buyer creates an offer for less that what minter is asking.
4080  uint256 const buyOfferIndex =
4081  keylet::nftoffer(buyer, env.seq(buyer)).key;
4082  env(token::createOffer(buyer, nftID, gwXAU(899)),
4083  token::owner(minter));
4084  env.close();
4085 
4086  // broker attempts to broker the offers but cannot.
4087  env(token::brokerOffers(
4088  broker, buyOfferIndex, minterOfferIndex),
4090  env.close();
4091 
4092  // Cancel buyer's bad offer so the next test starts in a
4093  // clean state.
4094  env(token::cancelOffer(buyer, {buyOfferIndex}));
4095  env.close();
4096  }
4097  // buyer creates a large enough offer.
4098  uint256 const buyOfferIndex =
4099  keylet::nftoffer(buyer, env.seq(buyer)).key;
4100  env(token::createOffer(buyer, nftID, gwXAU(1000)),
4101  token::owner(minter));
4102  env.close();
4103 
4104  // Broker attempts to charge a brokerFee larger than the
4105  // difference between the two offers but cannot.
4106  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4107  token::brokerFee(gwXAU(101)),
4109  env.close();
4110 
4111  // broker charges the full difference between the two offers and
4112  // succeeds.
4113  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4114  token::brokerFee(gwXAU(100)));
4115  env.close();
4116 
4117  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4118  BEAST_EXPECT(ownerCount(env, minter) == 1);
4119  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4120  BEAST_EXPECT(ownerCount(env, broker) == 1);
4121  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4122  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4123  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4124  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4125 
4126  // Burn the NFT so the next test starts with a clean state.
4127  env(token::burn(buyer, nftID));
4128  env.close();
4129  }
4130  // seller offers more than buyer is asking.
4131  // There are both transfer and broker fees, but broker takes less than
4132  // the maximum.
4133  {
4134  checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4135  setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4136 
4137  uint256 const nftID = mintNFT(maxTransferFee / 2); // 25%
4138 
4139  // minter creates their offer.
4140  uint256 const minterOfferIndex =
4141  keylet::nftoffer(minter, env.seq(minter)).key;
4142  env(token::createOffer(minter, nftID, gwXAU(900)),
4143  txflags(tfSellNFToken));
4144  env.close();
4145 
4146  // buyer creates a large enough offer.
4147  uint256 const buyOfferIndex =
4148  keylet::nftoffer(buyer, env.seq(buyer)).key;
4149  env(token::createOffer(buyer, nftID, gwXAU(1000)),
4150  token::owner(minter));
4151  env.close();
4152 
4153  // broker charges half difference between the two offers and
4154  // succeeds. 25% of the remaining difference goes to issuer.
4155  // The rest goes to minter.
4156  env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4157  token::brokerFee(gwXAU(50)));
4158  env.close();
4159 
4160  BEAST_EXPECT(ownerCount(env, issuer) == 1);
4161  BEAST_EXPECT(ownerCount(env, minter) == 1);
4162  BEAST_EXPECT(ownerCount(env, buyer) == 2);
4163  BEAST_EXPECT(ownerCount(env, broker) == 1);
4164  BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4165  BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4166  BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4167  BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4168 
4169  // Burn the NFT so the next test starts with a clean state.
4170  env(token::burn(buyer, nftID));
4171  env.close();
4172  }
4173  }
4174 
4175  void
4177  {
4178  // Verify the Owner field of an offer behaves as expected.
4179  testcase("NFToken offer owner");
4180 
4181  using namespace test::jtx;
4182 
4183  Env env{*this, features};
4184 
4185  Account const issuer{"issuer"};
4186  Account const buyer1{"buyer1"};
4187  Account const buyer2{"buyer2"};
4188  env.fund(XRP(10000), issuer, buyer1, buyer2);
4189  env.close();
4190 
4191  // issuer creates an NFT.
4192  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4193  env(token::mint(issuer, 0u), txflags(tfTransferable));
4194  env.close();
4195 
4196  // Prove that issuer now owns nftId.
4197  BEAST_EXPECT(nftCount(env, issuer) == 1);
4198  BEAST_EXPECT(nftCount(env, buyer1) == 0);
4199  BEAST_EXPECT(nftCount(env, buyer2) == 0);
4200 
4201  // Both buyer1 and buyer2 create buy offers for nftId.
4202  uint256 const buyer1OfferIndex =
4203  keylet::nftoffer(buyer1, env.seq(buyer1)).key;
4204  env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4205  uint256 const buyer2OfferIndex =
4206  keylet::nftoffer(buyer2, env.seq(buyer2)).key;
4207  env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4208  env.close();
4209 
4210  // Lambda that counts the number of buy offers for a given NFT.
4211  auto nftBuyOfferCount = [&env](uint256 const& nftId) -> std::size_t {
4212  // We know that in this case not very many offers will be
4213  // returned, so we skip the marker stuff.
4214  Json::Value params;
4215  params[jss::nft_id] = to_string(nftId);
4216  Json::Value buyOffers =
4217  env.rpc("json", "nft_buy_offers", to_string(params));
4218 
4219  if (buyOffers.isMember(jss::result) &&
4220  buyOffers[jss::result].isMember(jss::offers))
4221  return buyOffers[jss::result][jss::offers].size();
4222 
4223  return 0;
4224  };
4225 
4226  // Show there are two buy offers for nftId.
4227  BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4228 
4229  // issuer accepts buyer1's offer.
4230  env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4231  env.close();
4232 
4233  // Prove that buyer1 now owns nftId.
4234  BEAST_EXPECT(nftCount(env, issuer) == 0);
4235  BEAST_EXPECT(nftCount(env, buyer1) == 1);
4236  BEAST_EXPECT(nftCount(env, buyer2) == 0);
4237 
4238  // buyer1's offer was consumed, but buyer2's offer is still in the
4239  // ledger.
4240  BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4241 
4242  // buyer1 can now accept buyer2's offer, even though buyer2's
4243  // NFTokenCreateOffer transaction specified the NFT Owner as issuer.
4244  env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4245  env.close();
4246 
4247  // Prove that buyer2 now owns nftId.
4248  BEAST_EXPECT(nftCount(env, issuer) == 0);
4249  BEAST_EXPECT(nftCount(env, buyer1) == 0);
4250  BEAST_EXPECT(nftCount(env, buyer2) == 1);
4251 
4252  // All of the NFTokenOffers are now consumed.
4253  BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4254  }
4255 
4256  void
4258  {
4259  // Make sure all NFToken transactions work with tickets.
4260  testcase("NFToken transactions with tickets");
4261 
4262  using namespace test::jtx;
4263 
4264  Env env{*this, features};
4265 
4266  Account const issuer{"issuer"};
4267  Account const buyer{"buyer"};
4268  env.fund(XRP(10000), issuer, buyer);
4269  env.close();
4270 
4271  // issuer and buyer grab enough tickets for all of the following
4272  // transactions. Note that once the tickets are acquired issuer's
4273  // and buyer's account sequence numbers should not advance.
4274  std::uint32_t issuerTicketSeq{env.seq(issuer) + 1};
4275  env(ticket::create(issuer, 10));
4276  env.close();
4277  std::uint32_t const issuerSeq{env.seq(issuer)};
4278  BEAST_EXPECT(ticketCount(env, issuer) == 10);
4279 
4280  std::uint32_t buyerTicketSeq{env.seq(buyer) + 1};
4281  env(ticket::create(buyer, 10));
4282  env.close();
4283  std::uint32_t const buyerSeq{env.seq(buyer)};
4284  BEAST_EXPECT(ticketCount(env, buyer) == 10);
4285 
4286  // NFTokenMint
4287  BEAST_EXPECT(ownerCount(env, issuer) == 10);
4288  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4289  env(token::mint(issuer, 0u),
4290  txflags(tfTransferable),
4291  ticket::use(issuerTicketSeq++));
4292  env.close();
4293  BEAST_EXPECT(ownerCount(env, issuer) == 10);
4294  BEAST_EXPECT(ticketCount(env, issuer) == 9);
4295 
4296  // NFTokenCreateOffer
4297  BEAST_EXPECT(ownerCount(env, buyer) == 10);
4298  uint256 const offerIndex0 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4299  env(token::createOffer(buyer, nftId, XRP(1)),
4300  token::owner(issuer),
4301  ticket::use(buyerTicketSeq++));
4302  env.close();
4303  BEAST_EXPECT(ownerCount(env, buyer) == 10);
4304  BEAST_EXPECT(ticketCount(env, buyer) == 9);
4305 
4306  // NFTokenCancelOffer
4307  env(token::cancelOffer(buyer, {offerIndex0}),
4308  ticket::use(buyerTicketSeq++));
4309  env.close();
4310  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4311  BEAST_EXPECT(ticketCount(env, buyer) == 8);
4312 
4313  // NFTokenCreateOffer. buyer tries again.
4314  uint256 const offerIndex1 = keylet::nftoffer(buyer, buyerTicketSeq).key;
4315  env(token::createOffer(buyer, nftId, XRP(2)),
4316  token::owner(issuer),
4317  ticket::use(buyerTicketSeq++));
4318  env.close();
4319  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4320  BEAST_EXPECT(ticketCount(env, buyer) == 7);
4321 
4322  // NFTokenAcceptOffer. issuer accepts buyer's offer.
4323  env(token::acceptBuyOffer(issuer, offerIndex1),
4324  ticket::use(issuerTicketSeq++));
4325  env.close();
4326  BEAST_EXPECT(ownerCount(env, issuer) == 8);
4327  BEAST_EXPECT(ownerCount(env, buyer) == 8);
4328  BEAST_EXPECT(ticketCount(env, issuer) == 8);
4329 
4330  // NFTokenBurn. buyer burns the token they just bought.
4331  env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4332  env.close();
4333  BEAST_EXPECT(ownerCount(env, issuer) == 8);
4334  BEAST_EXPECT(ownerCount(env, buyer) == 6);
4335  BEAST_EXPECT(ticketCount(env, buyer) == 6);
4336 
4337  // Verify that the account sequence numbers did not advance.
4338  BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4339  BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4340  }
4341 
4342  void
4344  {
4345  // Account deletion rules with NFTs:
4346  // 1. An account holding one or more NFT offers may be deleted.
4347  // 2. An NFT issuer with any NFTs they have issued still in the
4348  // ledger may not be deleted.
4349  // 3. An account holding one or more NFTs may not be deleted.
4350  testcase("NFToken delete account");
4351 
4352  using namespace test::jtx;
4353 
4354  Env env{*this, features};
4355 
4356  Account const issuer{"issuer"};
4357  Account const minter{"minter"};
4358  Account const becky{"becky"};
4359  Account const carla{"carla"};
4360  Account const daria{"daria"};
4361 
4362  env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4363  env.close();
4364 
4365  // Allow enough ledgers to pass so any of these accounts can be deleted.
4366  for (int i = 0; i < 300; ++i)
4367  env.close();
4368 
4369  env(token::setMinter(issuer, minter));
4370  env.close();
4371 
4372  uint256 const nftId{token::getNextID(env, issuer, 0u, tfTransferable)};
4373  env(token::mint(minter, 0u),
4374  token::issuer(issuer),
4375  txflags(tfTransferable));
4376  env.close();
4377 
4378  // At the momement issuer and minter cannot delete themselves.
4379  // o issuer has an issued NFT in the ledger.
4380  // o minter owns an NFT.
4381  env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4382  env(acctdelete(minter, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4383  env.close();
4384 
4385  // Let enough ledgers pass so the account delete transactions are
4386  // not retried.
4387  for (int i = 0; i < 15; ++i)
4388  env.close();
4389 
4390  // becky and carla create offers for minter's NFT.
4391  env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4392  env.close();
4393 
4394  uint256 const carlaOfferIndex =
4395  keylet::nftoffer(carla, env.seq(carla)).key;
4396  env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4397  env.close();
4398 
4399  // It should be possible for becky to delete herself, even though
4400  // becky has an active NFT offer.
4401  env(acctdelete(becky, daria), fee(XRP(50)));
4402  env.close();
4403 
4404  // minter accepts carla's offer.
4405  env(token::acceptBuyOffer(minter, carlaOfferIndex));
4406  env.close();
4407 
4408  // Now it should be possible for minter to delete themselves since
4409  // they no longer own an NFT.
4410  env(acctdelete(minter, daria), fee(XRP(50)));
4411  env.close();
4412 
4413  // 1. issuer cannot delete themselves because they issued an NFT that
4414  // is still in the ledger.
4415  // 2. carla owns an NFT, so she cannot delete herself.
4416  env(acctdelete(issuer, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4417  env(acctdelete(carla, daria), fee(XRP(50)), ter(tecHAS_OBLIGATIONS));
4418  env.close();
4419 
4420  // Let enough ledgers pass so the account delete transactions are
4421  // not retried.
4422  for (int i = 0; i < 15; ++i)
4423  env.close();
4424 
4425  // carla burns her NFT. Since issuer's NFT is no longer in the
4426  // ledger, both issuer and carla can delete themselves.
4427  env(token::burn(carla, nftId));
4428  env.close();
4429 
4430  env(acctdelete(issuer, daria), fee(XRP(50)));
4431  env(acctdelete(carla, daria), fee(XRP(50)));
4432  env.close();
4433  }
4434 
4435  void
4437  {
4438  testcase("nft_buy_offers and nft_sell_offers");
4439 
4440  // The default limit on returned NFToken offers is 250, so we need
4441  // to produce more than 250 offers of each kind in order to exercise
4442  // the marker.
4443 
4444  // Fortunately there's nothing in the rules that says an account
4445  // can't hold more than one offer for the same NFT. So we only
4446  // need two accounts to generate the necessary offers.
4447  using namespace test::jtx;
4448 
4449  Env env{*this, features};
4450 
4451  Account const issuer{"issuer"};
4452  Account const buyer{"buyer"};
4453 
4454  // A lot of offers requires a lot for reserve.
4455  env.fund(XRP(1000000), issuer, buyer);
4456  env.close();
4457 
4458  // Create an NFT that we'll make offers for.
4459  uint256 const nftID{token::getNextID(env, issuer, 0u, tfTransferable)};
4460  env(token::mint(issuer, 0), txflags(tfTransferable));
4461  env.close();
4462 
4463  // A lambda that validates nft_XXX_offers query responses.
4464  auto checkOffers = [this, &env, &nftID](
4465  char const* request,
4466  int expectCount,
4467  int expectMarkerCount,
4468  int line) {
4469  int markerCount = 0;
4470  Json::Value allOffers(Json::arrayValue);
4471  std::string marker;
4472 
4473  // The do/while collects results until no marker is returned.
4474  do
4475  {
4476  Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4477  Json::Value params;
4478  params[jss::nft_id] = to_string(nftID);
4479 
4480  if (!marker.empty())
4481  params[jss::marker] = marker;
4482  return env.rpc("json", request, to_string(params));
4483  }();
4484 
4485  // If there are no offers for the NFT we get an error
4486  if (expectCount == 0)
4487  {
4488  if (expect(
4489  nftOffers.isMember(jss::result),
4490  "expected \"result\"",
4491  __FILE__,
4492  line))
4493  {
4494  if (expect(
4495  nftOffers[jss::result].isMember(jss::error),
4496  "expected \"error\"",
4497  __FILE__,
4498  line))
4499  {
4500  expect(
4501  nftOffers[jss::result][jss::error].asString() ==
4502  "objectNotFound",
4503  "expected \"objectNotFound\"",
4504  __FILE__,
4505  line);
4506  }
4507  }
4508  break;
4509  }
4510 
4511  marker.clear();
4512  if (expect(
4513  nftOffers.isMember(jss::result),
4514  "expected \"result\"",
4515  __FILE__,
4516  line))
4517  {
4518  Json::Value& result = nftOffers[jss::result];
4519 
4520  if (result.isMember(jss::marker))
4521  {
4522  ++markerCount;
4523  marker = result[jss::marker].asString();
4524  }
4525 
4526  if (expect(
4527  result.isMember(jss::offers),
4528  "expected \"offers\"",
4529  __FILE__,
4530  line))
4531  {
4532  Json::Value& someOffers = result[jss::offers];
4533  for (std::size_t i = 0; i < someOffers.size(); ++i)
4534  allOffers.append(someOffers[i]);
4535  }
4536  }
4537  } while (!marker.empty());
4538 
4539  // Verify the contents of allOffers makes sense.
4540  expect(
4541  allOffers.size() == expectCount,
4542  "Unexpected returned offer count",
4543  __FILE__,
4544  line);
4545  expect(
4546  markerCount == expectMarkerCount,
4547  "Unexpected marker count",
4548  __FILE__,
4549  line);
4550  std::optional<int> globalFlags;
4551  std::set<std::string> offerIndexes;
4552  std::set<std::string> amounts;
4553  for (Json::Value const& offer : allOffers)
4554  {
4555  // The flags on all found offers should be the same.
4556  if (!globalFlags)
4557  globalFlags = offer[jss::flags].asInt();
4558 
4559  expect(
4560  *globalFlags == offer[jss::flags].asInt(),
4561  "Inconsistent flags returned",
4562  __FILE__,
4563  line);
4564 
4565  // The test conditions should produce unique indexes and
4566  // amounts for all offers.
4567  offerIndexes.insert(offer[jss::nft_offer_index].asString());
4568  amounts.insert(offer[jss::amount].asString());
4569  }
4570 
4571  expect(
4572  offerIndexes.size() == expectCount,
4573  "Duplicate indexes returned?",
4574  __FILE__,
4575  line);
4576  expect(
4577  amounts.size() == expectCount,
4578  "Duplicate amounts returned?",
4579  __FILE__,
4580  line);
4581  };
4582 
4583  // There are no sell offers.
4584  checkOffers("nft_sell_offers", 0, false, __LINE__);
4585 
4586  // A lambda that generates sell offers.
4587  STAmount sellPrice = XRP(0);
4588  auto makeSellOffers =
4589  [&env, &issuer, &nftID, &sellPrice](STAmount const& limit) {
4590  // Save a little test time by not closing too often.
4591  int offerCount = 0;
4592  while (sellPrice < limit)
4593  {
4594  sellPrice += XRP(1);
4595  env(token::createOffer(issuer, nftID, sellPrice),
4596  txflags(tfSellNFToken));
4597  if (++offerCount % 10 == 0)
4598  env.close();
4599  }
4600  env.close();
4601  };
4602 
4603  // There is one sell offer.
4604  makeSellOffers(XRP(1));
4605  checkOffers("nft_sell_offers", 1, 0, __LINE__);
4606 
4607  // There are 250 sell offers.
4608  makeSellOffers(XRP(250));
4609  checkOffers("nft_sell_offers", 250, 0, __LINE__);
4610 
4611  // There are 251 sell offers.
4612  makeSellOffers(XRP(251));
4613  checkOffers("nft_sell_offers", 251, 1, __LINE__);
4614 
4615  // There are 500 sell offers.
4616  makeSellOffers(XRP(500));
4617  checkOffers("nft_sell_offers", 500, 1, __LINE__);
4618 
4619  // There are 501 sell offers.
4620  makeSellOffers(XRP(501));
4621  checkOffers("nft_sell_offers", 501, 2, __LINE__);
4622 
4623  // There are no buy offers.
4624  checkOffers("nft_buy_offers", 0, 0, __LINE__);
4625 
4626  // A lambda that generates buy offers.
4627  STAmount buyPrice = XRP(0);
4628  auto makeBuyOffers =
4629  [&env, &buyer, &issuer, &nftID, &buyPrice](STAmount const& limit) {
4630  // Save a little test time by not closing too often.
4631  int offerCount = 0;
4632  while (buyPrice < limit)
4633  {
4634  buyPrice += XRP(1);
4635  env(token::createOffer(buyer, nftID, buyPrice),
4636  token::owner(issuer));
4637  if (++offerCount % 10 == 0)
4638  env.close();
4639  }
4640  env.close();
4641  };
4642 
4643  // There is one buy offer;
4644  makeBuyOffers(XRP(1));
4645  checkOffers("nft_buy_offers", 1, 0, __LINE__);
4646 
4647  // There are 250 buy offers.
4648  makeBuyOffers(XRP(250));
4649  checkOffers("nft_buy_offers", 250, 0, __LINE__);
4650 
4651  // There are 251 buy offers.
4652  makeBuyOffers(XRP(251));
4653  checkOffers("nft_buy_offers", 251, 1, __LINE__);
4654 
4655  // There are 500 buy offers.
4656  makeBuyOffers(XRP(500));
4657  checkOffers("nft_buy_offers", 500, 1, __LINE__);
4658 
4659  // There are 501 buy offers.
4660  makeBuyOffers(XRP(501));
4661  checkOffers("nft_buy_offers", 501, 2, __LINE__);
4662  }
4663 
4664  void
4666  {
4667  // Exercise changes introduced by fixNFTokenNegOffer.
4668  using namespace test::jtx;
4669 
4670  testcase("fixNFTokenNegOffer");
4671 
4672  Account const issuer{"issuer"};
4673  Account const buyer{"buyer"};
4674  Account const gw{"gw"};
4675  IOU const gwXAU(gw["XAU"]);
4676 
4677  // Test both with and without fixNFTokenNegOffer
4678  for (auto const& tweakedFeatures :
4680  features | fixNFTokenNegOffer})
4681  {
4682  // There was a bug in the initial NFT implementation that
4683  // allowed offers to be placed with negative amounts. Verify
4684  // that fixNFTokenNegOffer addresses the problem.
4685  Env env{*this, tweakedFeatures};
4686 
4687  env.fund(XRP(1000000), issuer, buyer, gw);
4688  env.close();
4689 
4690  env(trust(issuer, gwXAU(2000)));
4691  env(trust(buyer, gwXAU(2000)));
4692  env.close();
4693 
4694  env(pay(gw, issuer, gwXAU(1000)));
4695  env(pay(gw, buyer, gwXAU(1000)));
4696  env.close();
4697 
4698  // Create an NFT that we'll make XRP offers for.
4699  uint256 const nftID0{
4700  token::getNextID(env, issuer, 0u, tfTransferable)};
4701  env(token::mint(issuer, 0), txflags(tfTransferable));
4702  env.close();
4703 
4704  // Create an NFT that we'll make IOU offers for.
4705  uint256 const nftID1{
4706  token::getNextID(env, issuer, 1u, tfTransferable)};
4707  env(token::mint(issuer, 1), txflags(tfTransferable));
4708  env.close();
4709 
4710  TER const offerCreateTER = tweakedFeatures[fixNFTokenNegOffer]
4711  ? static_cast<TER>(temBAD_AMOUNT)
4712  : static_cast<TER>(tesSUCCESS);
4713 
4714  // Make offers with negative amounts for the NFTs
4715  uint256 const sellNegXrpOfferIndex =
4716  keylet::nftoffer(issuer, env.seq(issuer)).key;
4717  env(token::createOffer(issuer, nftID0, XRP(-2)),
4718  txflags(tfSellNFToken),
4719  ter(offerCreateTER));
4720  env.close();
4721 
4722  uint256 const sellNegIouOfferIndex =
4723  keylet::nftoffer(issuer, env.seq(issuer)).key;
4724  env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4725  txflags(tfSellNFToken),
4726  ter(offerCreateTER));
4727  env.close();
4728 
4729  uint256 const buyNegXrpOfferIndex =
4730  keylet::nftoffer(buyer, env.seq(buyer)).key;
4731  env(token::createOffer(buyer, nftID0, XRP(-1)),
4732  token::owner(issuer),
4733  ter(offerCreateTER));
4734  env.close();
4735 
4736  uint256 const buyNegIouOfferIndex =
4737  keylet::nftoffer(buyer, env.seq(buyer)).key;
4738  env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4739  token::owner(issuer),
4740  ter(offerCreateTER));
4741  env.close();
4742 
4743  {
4744  // Now try to accept the offers.
4745  // 1. If fixNFTokenNegOffer is NOT enabled get tecINTERNAL.
4746  // 2. If fixNFTokenNegOffer IS enabled get tecOBJECT_NOT_FOUND.
4747  TER const offerAcceptTER = tweakedFeatures[fixNFTokenNegOffer]
4748  ? static_cast<TER>(tecOBJECT_NOT_FOUND)
4749  : static_cast<TER>(tecINTERNAL);
4750 
4751  // Sell offers.
4752  env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4753  ter(offerAcceptTER));
4754  env.close();
4755  env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4756  ter(offerAcceptTER));
4757  env.close();
4758 
4759  // Buy offers.
4760  env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
4761  ter(offerAcceptTER));
4762  env.close();
4763  env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
4764  ter(offerAcceptTER));
4765  env.close();
4766  }
4767  {
4768  // 1. If fixNFTokenNegOffer is NOT enabled get tecSUCCESS.
4769  // 2. If fixNFTokenNegOffer IS enabled get tecOBJECT_NOT_FOUND.
4770  TER const offerAcceptTER = tweakedFeatures[fixNFTokenNegOffer]
4771  ? static_cast<TER>(tecOBJECT_NOT_FOUND)
4772  : static_cast<TER>(tesSUCCESS);
4773 
4774  // Brokered offers.
4775  env(token::brokerOffers(
4776  gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
4777  ter(offerAcceptTER));
4778  env.close();
4779  env(token::brokerOffers(
4780  gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
4781  ter(offerAcceptTER));
4782  env.close();
4783  }
4784  }
4785 
4786  // Test what happens if NFTokenOffers are created with negative amounts
4787  // and then fixNFTokenNegOffer goes live. What does an acceptOffer do?
4788  {
4789  Env env{
4790  *this,
4792 
4793  env.fund(XRP(1000000), issuer, buyer, gw);
4794  env.close();
4795 
4796  env(trust(issuer, gwXAU(2000)));
4797  env(trust(buyer, gwXAU(2000)));
4798  env.close();
4799 
4800  env(pay(gw, issuer, gwXAU(1000)));
4801  env(pay(gw, buyer, gwXAU(1000)));
4802  env.close();
4803 
4804  // Create an NFT that we'll make XRP offers for.
4805  uint256 const nftID0{
4806  token::getNextID(env, issuer, 0u, tfTransferable)};
4807  env(token::mint(issuer, 0), txflags(tfTransferable));
4808  env.close();
4809 
4810  // Create an NFT that we'll make IOU offers for.
4811  uint256 const nftID1{
4812  token::getNextID(env, issuer, 1u, tfTransferable)};
4813  env(token::mint(issuer, 1), txflags(tfTransferable));
4814  env.close();
4815 
4816  // Make offers with negative amounts for the NFTs
4817  uint256 const sellNegXrpOfferIndex =
4818  keylet::nftoffer(issuer, env.seq(issuer)).key;
4819  env(token::createOffer(issuer, nftID0, XRP(-2)),
4820  txflags(tfSellNFToken));
4821  env.close();
4822 
4823  uint256 const sellNegIouOfferIndex =
4824  keylet::nftoffer(issuer, env.seq(issuer)).key;
4825  env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4826  txflags(tfSellNFToken));
4827  env.close();
4828 
4829  uint256 const buyNegXrpOfferIndex =
4830  keylet::nftoffer(buyer, env.seq(buyer)).key;
4831  env(token::createOffer(buyer, nftID0, XRP(-1)),
4832  token::owner(issuer));
4833  env.close();
4834 
4835  uint256 const buyNegIouOfferIndex =
4836  keylet::nftoffer(buyer, env.seq(buyer)).key;
4837  env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4838  token::owner(issuer));
4839  env.close();
4840 
4841  // Now the amendment passes.
4842  env.enableFeature(fixNFTokenNegOffer);
4843  env.close();
4844 
4845  // All attempts to accept the offers with negative amounts
4846  // should fail with temBAD_OFFER.
4847  env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4848  ter(temBAD_OFFER));
4849  env.close();
4850  env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4851  ter(temBAD_OFFER));
4852  env.close();
4853 
4854  // Buy offers.
4855  env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
4856  ter(temBAD_OFFER));
4857  env.close();
4858  env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
4859  ter(temBAD_OFFER));
4860  env.close();
4861 
4862  // Brokered offers.
4863  env(token::brokerOffers(
4864  gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
4865  ter(temBAD_OFFER));
4866  env.close();
4867  env(token::brokerOffers(
4868  gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
4869  ter(temBAD_OFFER));
4870  env.close();
4871  }
4872 
4873  // Test buy offers with a destination with and without
4874  // fixNFTokenNegOffer.
4875  for (auto const& tweakedFeatures :
4877  features | fixNFTokenNegOffer})
4878  {
4879  Env env{*this, tweakedFeatures};
4880 
4881  env.fund(XRP(1000000), issuer, buyer);
4882 
4883  // Create an NFT that we'll make offers for.
4884  uint256 const nftID{
4885  token::getNextID(env, issuer, 0u, tfTransferable)};
4886  env(token::mint(issuer, 0), txflags(tfTransferable));
4887  env.close();
4888 
4889  TER const offerCreateTER = tweakedFeatures[fixNFTokenNegOffer]
4890  ? static_cast<TER>(tesSUCCESS)
4891  : static_cast<TER>(temMALFORMED);
4892 
4893  env(token::createOffer(buyer, nftID, drops(1)),
4894  token::owner(issuer),
4895  token::destination(issuer),
4896  ter(offerCreateTER));
4897  env.close();
4898  }
4899  }
4900 
4901  void
4903  {
4904  testEnabled(features);
4905  testMintReserve(features);
4906  testMintMaxTokens(features);
4907  testMintInvalid(features);
4908  testBurnInvalid(features);
4909  testCreateOfferInvalid(features);
4910  testCancelOfferInvalid(features);
4911  testAcceptOfferInvalid(features);
4912  testMintFlagBurnable(features);
4913  testMintFlagOnlyXRP(features);
4914  testMintFlagCreateTrustLine(features);
4915  testMintFlagTransferable(features);
4916  testMintTransferFee(features);
4917  testMintTaxon(features);
4918  testMintURI(features);
4919  testCreateOfferDestination(features);
4920  testCreateOfferExpiration(features);
4921  testCancelOffers(features);
4922  testCancelTooManyOffers(features);
4923  testBrokeredAccept(features);
4924  testNFTokenOfferOwner(features);
4925  testNFTokenWithTickets(features);
4926  testNFTokenDeleteAccount(features);
4927  testNftXxxOffers(features);
4928  testFixNFTokenNegOffer(features);
4929  }
4930 
4931 public:
4932  void
4933  run() override
4934  {
4935  using namespace test::jtx;
4936  FeatureBitset const all{supported_amendments()};
4937  FeatureBitset const fixNFTDir{fixNFTokenDirV1};
4938 
4939  testWithFeats(all - fixNFTDir);
4940  testWithFeats(all);
4941  }
4942 };
4943 
4944 BEAST_DEFINE_TESTSUITE_PRIO(NFToken, tx, ripple, 2);
4945 
4946 } // namespace ripple
ripple::NFToken_test::testNFTokenDeleteAccount
void testNFTokenDeleteAccount(FeatureBitset features)
Definition: NFToken_test.cpp:4343
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:1358
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:2967
ripple::NFToken_test::testNFTokenOfferOwner
void testNFTokenOfferOwner(FeatureBitset features)
Definition: NFToken_test.cpp:4176
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:3555
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:1476
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:3441
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:4902
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:4665
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:4933
ripple::JsonOptions::none
@ none
ripple::TERSubset< CanCvtToTER >
ripple::NFToken_test::testNftXxxOffers
void testNftXxxOffers(FeatureBitset features)
Definition: NFToken_test.cpp:4436
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:2483
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:2052
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:1762
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:3674
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:1570
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:4257
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:2556
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:219
ripple::NFToken_test::testCreateOfferDestination
void testCreateOfferDestination(FeatureBitset features)
Definition: NFToken_test.cpp:2666
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