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