1#include <test/jtx/AMM.h>
2#include <test/jtx/AMMTest.h>
3#include <test/jtx/Account.h>
4#include <test/jtx/Env.h>
5#include <test/jtx/TestHelpers.h>
6#include <test/jtx/amount.h>
7#include <test/jtx/balance.h>
8#include <test/jtx/credentials.h>
9#include <test/jtx/domain.h>
10#include <test/jtx/fee.h>
11#include <test/jtx/jtx_json.h>
12#include <test/jtx/ledgerStateFix.h>
13#include <test/jtx/offer.h>
14#include <test/jtx/owners.h>
15#include <test/jtx/paths.h>
16#include <test/jtx/pay.h>
17#include <test/jtx/permissioned_dex.h>
18#include <test/jtx/permissioned_domains.h>
19#include <test/jtx/sendmax.h>
20#include <test/jtx/ter.h>
21#include <test/jtx/trust.h>
22#include <test/jtx/txflags.h>
24#include <xrpl/basics/base_uint.h>
25#include <xrpl/beast/unit_test/suite.h>
26#include <xrpl/beast/utility/Journal.h>
27#include <xrpl/ledger/OpenView.h>
28#include <xrpl/protocol/Book.h>
29#include <xrpl/protocol/Feature.h>
30#include <xrpl/protocol/Indexes.h>
31#include <xrpl/protocol/Issue.h>
32#include <xrpl/protocol/Keylet.h>
33#include <xrpl/protocol/LedgerFormats.h>
34#include <xrpl/protocol/SField.h>
35#include <xrpl/protocol/STAmount.h>
36#include <xrpl/protocol/STArray.h>
37#include <xrpl/protocol/STLedgerEntry.h>
38#include <xrpl/protocol/TER.h>
39#include <xrpl/protocol/TxFlags.h>
40#include <xrpl/protocol/jss.h>
58 [[nodiscard]]
static bool
61 return static_cast<bool>(env.
le(
keylet::offer(account.id(), offerSeq)));
64 [[nodiscard]]
static bool
71 uint32_t
const flags = 0,
72 bool const domainOffer =
false)
75 uint64_t
const pageIndex,
81 if (domain != (*page)[~sfDomainID])
84 auto const& indexes = page->getFieldV256(sfIndexes);
85 for (
auto const& index : indexes)
97 if (sle->getFieldAmount(sfTakerGets) != takerGets)
99 if (sle->getFieldAmount(sfTakerPays) != takerPays)
101 if (sle->getFlags() != flags)
103 if (domainOffer && !sle->isFieldPresent(sfDomainID))
105 if (!domainOffer && sle->isFieldPresent(sfDomainID))
108 sle->getFieldH256(sfBookDirectory),
109 sle->getFieldU64(sfBookNode),
110 (*sle)[~sfDomainID]))
113 if (sle->isFlag(lsfHybrid))
115 if (!sle->isFieldPresent(sfDomainID))
117 if (!sle->isFieldPresent(sfAdditionalBooks))
119 if (sle->getFieldArray(sfAdditionalBooks).size() != 1)
122 auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks);
124 for (
auto const& bookDir : additionalBookDirs)
126 auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory);
127 auto const& dirNode = bookDir.getFieldU64(sfBookNode);
131 if (!offerInDir(dirIndex, dirNode, std::nullopt))
137 if (sle->isFieldPresent(sfAdditionalBooks))
154 return Keylet(ltDIR_NODE, (*sle)[sfBookDirectory]).
key;
159 [[nodiscard]]
static bool
167 auto const page = env.
le(
172 pageIndex = (*page)[~sfIndexNext];
173 dirCnt += (*page)[sfIndexes].size();
175 }
while (pageIndex.
value_or(0) != 0u);
177 return dirCnt == dirSize;
187 Env env(*
this, features - featurePermissionedDEX);
188 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
204 if (features[fixCleanup3_2_0])
206 Env env(*
this, features);
207 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
216 Env env(*
this, features);
217 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
224 env.
trust(USD(1000), devin);
226 env(
pay(gw, devin, USD(100)));
249 Env env(*
this, features);
250 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
257 env.
trust(USD(1000), devin);
259 env(
pay(gw, devin, USD(100)));
263 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
264 jv[sfExpiration.jsonName] = t + 20;
284 Env env(*
this, features);
285 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
288 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
298 Env env(*
this, features);
299 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
305 auto const bobOfferSeq{env.
seq(bob)};
309 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
315 Env env(*
this, features);
316 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
322 auto const bobOfferSeq{env.
seq(bob)};
326 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq, USD(10),
XRP(10), 0,
true));
331 Env env(*
this, features);
332 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
335 auto const bobOfferSeq{env.
seq(bob)};
339 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
343 env(
offer(carol, USD(10),
XRP(10)));
346 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
348 auto const aliceOfferSeq{env.
seq(alice)};
352 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
359 Env env(*
this, features);
360 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
366 for (
size_t i = 0; i <= 100; i++)
368 auto const bobOfferSeq{env.
seq(bob)};
373 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
376 for (
auto const offerSeq : offerSeqs)
392 Env env(*
this, features - featurePermissionedDEX);
393 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
396 env(
pay(bob, alice, USD(10)),
417 if (features[fixCleanup3_2_0])
419 Env env(*
this, features);
420 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
423 env(
pay(bob_, alice_, USD(10)),
433 Env env(*
this, features);
434 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
437 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
440 env(
pay(bob, alice, USD(10)),
450 Env env(*
this, features);
451 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
461 env.
trust(USD(1000), devin);
463 env(
pay(gw, devin, USD(100)));
467 env(
pay(alice, devin, USD(10)),
479 env(
pay(alice, devin, USD(10)),
496 Env env(*
this, features);
497 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
507 env.
trust(USD(1000), devin);
509 env(
pay(gw, devin, USD(100)));
513 env(
pay(devin, alice, USD(10)),
525 env(
pay(devin, alice, USD(10)),
542 Env env(*
this, features);
543 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
569 Env env(*
this, features);
570 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
574 auto const regularOfferSeq{env.
seq(bob)};
577 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
580 BEAST_EXPECT(regularDirKey);
582 env, *regularDirKey, 1));
585 env(
pay(alice, carol, USD(10)),
593 auto const domainOfferSeq{env.
seq(bob)};
597 BEAST_EXPECT(
checkOffer(env, bob, domainOfferSeq,
XRP(10), USD(10), 0,
true));
600 BEAST_EXPECT(domainDirKey);
602 env, *domainDirKey, 1));
608 BEAST_EXPECT(!
offerExists(env, bob, domainOfferSeq));
609 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
613 env, *domainDirKey, 0));
615 env, *regularDirKey, 1));
620 Env env(*
this, features);
621 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
624 auto const eur = gw[
"EUR"];
625 env.
trust(eur(1000), alice);
627 env.
trust(eur(1000), bob);
629 env.
trust(eur(1000), carol);
631 env(
pay(gw, bob, eur(100)));
635 auto const usdOfferSeq{env.
seq(bob)};
639 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
642 env(
pay(alice, carol, eur(10)),
648 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
651 auto const regularOfferSeq{env.
seq(bob)};
652 env(
offer(bob, USD(10), eur(10)));
654 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq, USD(10), eur(10)));
658 env(
pay(alice, carol, eur(10)),
666 auto const eurOfferSeq{env.
seq(bob)};
667 env(
offer(bob, USD(10), eur(10)),
Domain(domainID));
669 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), 0,
true));
675 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(5), USD(5), 0,
true));
676 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), 0,
true));
687 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq, USD(10), eur(10)));
692 Env env(*
this, features);
693 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
697 Account const badDomainOwner(
"badDomainOwner");
699 env.
fund(
XRP(1000), badDomainOwner, devin);
701 env.
trust(USD(1000), devin);
703 env(
pay(gw, devin, USD(100)));
706 auto const badCredType =
"badCred";
708 {.issuer = badDomainOwner, .credType = badCredType}};
712 auto const badDomainID = objects.begin()->first;
723 env(
pay(alice, carol, USD(10)),
731 auto const bobOfferSeq{env.
seq(bob)};
734 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
747 Env env(*
this, features);
748 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
773 Env env(*
this, features);
774 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
781 env.
trust(USD(1000), devin);
783 env(
pay(gw, devin, USD(100)));
787 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
788 jv[sfExpiration.jsonName] = t + 20;
795 auto const offerSeq{env.
seq(devin)};
802 BEAST_EXPECT(
checkOffer(env, devin, offerSeq,
XRP(5), USD(5), 0,
true));
808 env(
pay(alice, carol, USD(5)),
814 BEAST_EXPECT(
checkOffer(env, devin, offerSeq,
XRP(5), USD(5), 0,
true));
819 Env env(*
this, features);
820 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
823 auto const offerSeq{env.
seq(bob)};
830 BEAST_EXPECT(
checkOffer(env, bob, offerSeq,
XRP(5), USD(5), 0,
true));
837 env(
pay(alice, carol, USD(5)),
843 BEAST_EXPECT(
checkOffer(env, bob, offerSeq,
XRP(5), USD(5), 0,
true));
855 Env env(*
this, features);
856 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
859 auto const eura = alice[
"EUR"];
860 auto const eurb = bob[
"EUR"];
862 env.
trust(eura(100), bob);
863 env.
trust(eurb(100), carol);
877 env(
trust(carol, bob[
"EUR"](0), bob, tfSetNoRipple));
889 testcase(
"Offer token issuer in domain");
893 Env env(*
this, features);
894 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
898 auto const bobOffer1Seq{env.
seq(bob)};
903 auto const bobOffer2Seq{env.
seq(bob)};
907 BEAST_EXPECT(
checkOffer(env, bob, bobOffer1Seq,
XRP(10), USD(10), 0,
true));
908 BEAST_EXPECT(
checkOffer(env, bob, bobOffer2Seq, USD(10),
XRP(10), lsfPassive,
true));
918 BEAST_EXPECT(!
offerExists(env, bob, bobOffer1Seq));
924 BEAST_EXPECT(!
offerExists(env, bob, bobOffer2Seq));
934 Env env(*
this, features);
935 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
938 auto const aliceOfferSeq{env.
seq(alice)};
942 auto const bobOfferSeq{env.
seq(bob)};
946 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(20), USD(20), 0,
true));
947 BEAST_EXPECT(
checkOffer(env, alice, aliceOfferSeq,
XRP(100), USD(100), 0,
true));
950 BEAST_EXPECT(domainDirKey);
952 env, *domainDirKey, 2));
961 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
964 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
966 env, *domainDirKey, 1));
974 Env env(*
this, features);
975 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
977 AMM const amm(env, alice,
XRP(10), USD(50));
980 env(
pay(bob, carol, USD(5)),
992 auto [xrp, usd, lpt] =
amm.balances(
XRP, USD);
993 BEAST_EXPECT(usd == USD(45));
999 bool const excludesAmmFromDomainQuality = features[fixCleanup3_3_0];
1001 testcase <<
"AMM quality not leaked into domain BookStep"
1002 << (excludesAmmFromDomainQuality ?
" (Cleanup3_3_0 enabled)"
1003 :
" (Cleanup3_3_0 disabled)");
1005 Env env(*
this, features);
1006 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1008 auto const eur = gw[
"EUR"];
1010 env.trust(eur(1000), bob, domainOwner);
1012 env(
pay(gw, bob, eur(100)));
1015 env(
pay(gw, alice, USD(500)));
1021 AMM const amm(env, alice,
XRP(10), USD(500));
1023 auto const directOfferSeq{env.seq(bob)};
1027 auto const xrpEurOfferSeq{env.seq(bob)};
1031 auto const eurUsdOfferSeq{env.seq(domainOwner)};
1032 env(
offer(domainOwner, eur(20), USD(20)),
Domain(domainID));
1035 auto const carolBalBefore = env.balance(carol, USD);
1042 env(
pay(alice, carol, USD(100)),
1046 Txflags(tfPartialPayment | tfNoRippleDirect),
1050 auto const delivered = env.balance(carol, USD) - carolBalBefore;
1051 if (excludesAmmFromDomainQuality)
1053 BEAST_EXPECT(delivered == USD(20));
1055 BEAST_EXPECT(
checkOffer(env, bob, directOfferSeq,
XRP(10), USD(10), 0,
true));
1056 BEAST_EXPECT(!
offerExists(env, bob, xrpEurOfferSeq));
1057 BEAST_EXPECT(!
offerExists(env, domainOwner, eurUsdOfferSeq));
1061 BEAST_EXPECT(delivered == USD(10));
1063 BEAST_EXPECT(!
offerExists(env, bob, directOfferSeq));
1064 BEAST_EXPECT(
checkOffer(env, bob, xrpEurOfferSeq,
XRP(10), eur(20), 0,
true));
1065 BEAST_EXPECT(
checkOffer(env, domainOwner, eurUsdOfferSeq, eur(20), USD(20), 0,
true));
1068 auto [xrp, usd, lpt] =
amm.balances(
XRP, USD);
1069 BEAST_EXPECT(xrp ==
XRP(10));
1070 BEAST_EXPECT(usd == USD(500));
1080 Env env(*
this, features - featurePermissionedDEX);
1081 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1101 auto const offerSeq{env.
seq(bob)};
1104 BEAST_EXPECT(
checkOffer(env, bob, offerSeq,
XRP(10), USD(10), lsfHybrid,
true));
1109 Env env(*
this, features);
1110 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1113 auto const bobOfferSeq{env.
seq(bob)};
1117 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1121 auto const aliceOfferSeq{env.
seq(alice)};
1125 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
1126 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1132 Env env(*
this, features);
1133 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1136 auto const bobOfferSeq{env.
seq(bob)};
1142 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1144 auto const aliceOfferSeq{env.
seq(alice)};
1145 env(
offer(alice, USD(10),
XRP(10)));
1148 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
1149 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1156 Env env(*
this, features);
1157 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1160 auto const bobOfferSeq{env.
seq(bob)};
1164 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
1168 auto const aliceOfferSeq{env.
seq(alice)};
1172 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
1173 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1180 Env env(*
this, features);
1181 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1184 auto const bobOfferSeq{env.
seq(bob)};
1188 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
false));
1192 auto const aliceOfferSeq{env.
seq(alice)};
1196 BEAST_EXPECT(
offerExists(env, alice, aliceOfferSeq));
1198 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
false));
1199 BEAST_EXPECT(
checkOffer(env, alice, aliceOfferSeq, USD(10),
XRP(10), lsfHybrid,
true));
1212 Env env(*
this, features);
1213 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1216 auto const hybridOfferSeq{env.
seq(bob)};
1226 env(
pay(alice, carol, USD(5)),
1232 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(50), USD(50), lsfHybrid,
true));
1234 if (features[fixCleanup3_3_0])
1238 auto const carolBalBefore = env.
balance(carol, USD);
1241 BEAST_EXPECT(env.
balance(carol, USD) - carolBalBefore == USD(5));
1242 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(45), USD(45), lsfHybrid,
true));
1245 auto const regularOfferSeq{env.
seq(bob)};
1248 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
1250 auto const sleHybridOffer = env.
le(
keylet::offer(bob.id(), hybridOfferSeq));
1251 if (!BEAST_EXPECT(sleHybridOffer))
1253 auto const openDir =
1254 sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory);
1263 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(40), USD(40), lsfHybrid,
true));
1264 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
1274 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(50), USD(50), lsfHybrid,
true));
1277 auto const regularOfferSeq{env.
seq(bob)};
1280 BEAST_EXPECT(
offerExists(env, bob, regularOfferSeq));
1281 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
1283 auto const sleHybridOffer = env.
le(
keylet::offer(bob.id(), hybridOfferSeq));
1284 if (!BEAST_EXPECT(sleHybridOffer))
1286 auto const openDir =
1287 sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory);
1296 BEAST_EXPECT(!
offerExists(env, bob, hybridOfferSeq));
1297 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(5), USD(5)));
1309 Env env(*
this, features);
1310 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1313 auto const hybridOfferSeq{env.
seq(bob)};
1319 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(5), USD(5), lsfHybrid,
true));
1325 BEAST_EXPECT(!
offerExists(env, bob, hybridOfferSeq));
1331 Env env(*
this, features);
1332 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1336 Account const badDomainOwner(
"badDomainOwner");
1338 env.
fund(
XRP(1000), badDomainOwner, devin);
1341 auto const badCredType =
"badCred";
1343 {.issuer = badDomainOwner, .credType = badCredType}};
1347 auto const badDomainID = objects.begin()->first;
1354 auto const hybridOfferSeq{env.
seq(bob)};
1359 env(
pay(devin, badDomainOwner, USD(5)),
1365 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1369 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(5), USD(5), lsfHybrid,
true));
1375 BEAST_EXPECT(!
offerExists(env, bob, hybridOfferSeq));
1380 Env env(*
this, features);
1381 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1384 auto const eur = gw[
"EUR"];
1385 env.
trust(eur(1000), alice);
1387 env.
trust(eur(1000), bob);
1389 env.
trust(eur(1000), carol);
1391 env(
pay(gw, bob, eur(100)));
1394 auto const usdOfferSeq{env.
seq(bob)};
1398 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
1401 env(
pay(alice, carol, eur(5)),
1407 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
1410 auto const eurOfferSeq{env.
seq(bob)};
1413 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), lsfHybrid,
true));
1419 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(5), USD(5), 0,
true));
1420 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), lsfHybrid,
true));
1425 Env env(*
this, features);
1426 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1429 auto const eur = gw[
"EUR"];
1430 env.
trust(eur(1000), alice);
1432 env.
trust(eur(1000), bob);
1434 env.
trust(eur(1000), carol);
1436 env(
pay(gw, bob, eur(100)));
1440 auto const usdOfferSeq{env.
seq(bob)};
1444 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
false));
1447 auto const eurOfferSeq{env.
seq(bob)};
1450 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), lsfHybrid,
true));
1456 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(5), USD(5), 0,
false));
1457 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), lsfHybrid,
true));
1468 testcase(
"Hybrid open book after credential expiry");
1470 Env env(*
this, features);
1471 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1477 env.
trust(USD(1000), devin);
1479 env(
pay(gw, devin, USD(100)));
1485 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
1486 jv[sfExpiration.jsonName] = t + 100;
1494 auto const hybridOfferSeq{env.
seq(devin)};
1498 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1502 auto carolBalance = env.
balance(carol, USD);
1505 BEAST_EXPECT(env.
balance(carol, USD) - carolBalance == USD(5));
1506 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(5), USD(5), lsfHybrid,
true));
1516 BEAST_EXPECT(
offerExists(env, devin, hybridOfferSeq));
1520 carolBalance = env.
balance(carol, USD);
1525 BEAST_EXPECT(env.
balance(carol, USD) - carolBalance == USD(2));
1527 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(3), USD(3), lsfHybrid,
true));
1532 env(
pay(alice, carol, USD(1)),
1541 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(3), USD(3), lsfHybrid,
true));
1544 carolBalance = env.
balance(carol, USD);
1547 BEAST_EXPECT(env.
balance(carol, USD) - carolBalance == USD(3));
1548 BEAST_EXPECT(!
offerExists(env, devin, hybridOfferSeq));
1554 Env env(*
this, features);
1555 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1567 size_t dirCnt = 100;
1569 for (
size_t i = 1; i <= dirCnt; i++)
1571 auto const bobOfferSeq{env.
seq(bob)};
1577 BEAST_EXPECT(sleOffer);
1578 BEAST_EXPECT(sleOffer->getFieldH256(sfBookDirectory) == domainDir);
1579 BEAST_EXPECT(sleOffer->getFieldArray(sfAdditionalBooks).size() == 1);
1581 sleOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory) ==
1584 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1589 for (
auto const offerSeq : offerSeqs)
1605 Env env(*
this, features);
1606 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1608 auto const eur = gw[
"EUR"];
1610 for (
auto const& account : {alice, bob, carol})
1612 env(
trust(account, eur(10000)));
1616 env(
pay(gw, carol, eur(1)));
1619 auto const aliceOfferSeq{env.
seq(alice)};
1620 auto const bobOfferSeq{env.
seq(bob)};
1627 auto const carolOfferSeq{env.
seq(carol)};
1628 env(
offer(carol, USD(1), eur(1)),
Domain(domainID));
1631 BEAST_EXPECT(!
offerExists(env, bob, aliceOfferSeq));
1632 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1633 BEAST_EXPECT(!
offerExists(env, bob, carolOfferSeq));
1639 bool const fixEnabled = features[fixCleanup3_1_3];
1641 testcase <<
"Hybrid offer with empty AdditionalBooks"
1642 << (fixEnabled ?
" (fixCleanup3_1_3 enabled)" :
" (fixCleanup3_1_3 disabled)");
1659 Env env(*
this, features);
1660 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1664 auto const bobOfferSeq{env.seq(bob)};
1674 auto const sle = view.
read(offerKey);
1678 replacement->setFieldArray(sfAdditionalBooks,
STArray{});
1687 env(
pay(alice, carol, USD(10)),
1705 bool const fixEnabled = features[fixCleanup3_2_0];
1706 testcase <<
"Hybrid offer crossing quality"
1707 << (fixEnabled ?
" (fixCleanup3_2_0)" :
" (pre-fix)");
1722 Env env(*
this, features);
1723 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
1727 auto const bobOfferSeq{env.seq(bob_)};
1730 BEAST_EXPECT(
offerExists(env, bob_, bobOfferSeq));
1736 auto const aliceOfferSeq{env.seq(alice_)};
1741 auto const sle = env.le(
keylet::offer(alice_.id(), aliceOfferSeq));
1743 BEAST_EXPECT(sle->isFieldPresent(sfAdditionalBooks));
1744 BEAST_EXPECT(sle->getFieldArray(sfAdditionalBooks).size() == 1);
1746 auto const domainDirKey = sle->getFieldH256(sfBookDirectory);
1747 auto const openDirKey =
1748 sle->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory);
1750 auto const domainQuality =
getQuality(domainDirKey);
1751 auto const openQuality =
getQuality(openDirKey);
1754 auto const domainDirSle = env.le(
Keylet(ltDIR_NODE, domainDirKey));
1755 auto const openDirSle = env.le(
Keylet(ltDIR_NODE, openDirKey));
1756 BEAST_EXPECT(domainDirSle);
1757 BEAST_EXPECT(openDirSle);
1759 auto const domainExRate = domainDirSle->getFieldU64(sfExchangeRate);
1760 auto const openExRate = openDirSle->getFieldU64(sfExchangeRate);
1761 auto const preCrossingQuality =
std::uint64_t{5623825668291712342ULL};
1762 auto const postCrossingQuality =
std::uint64_t{5623825668291712341ULL};
1766 BEAST_EXPECT(domainQuality == preCrossingQuality);
1767 BEAST_EXPECT(domainExRate == preCrossingQuality);
1768 BEAST_EXPECT(domainExRate == domainQuality);
1773 BEAST_EXPECT(openQuality == preCrossingQuality);
1774 BEAST_EXPECT(domainQuality == openQuality);
1777 BEAST_EXPECT(openExRate == preCrossingQuality);
1778 BEAST_EXPECT(openExRate == openQuality);
1784 BEAST_EXPECT(openQuality == postCrossingQuality);
1785 BEAST_EXPECT(domainQuality != openQuality);
1790 BEAST_EXPECT(openExRate == preCrossingQuality);
1791 BEAST_EXPECT(openExRate != openQuality);
1792 BEAST_EXPECT(openExRate == domainQuality);
1799 testcase(
"LedgerStateFix BookExchangeRate");
1816 Env env(*
this, features - fixCleanup3_2_0);
1828 Env env(*
this, features);
1836 missingBookDirectory.
removeMember(sfBookDirectory.jsonName);
1842 extraOwner[sfOwner.jsonName] = carol.
human();
1847 Env env(*
this, features);
1849 auto const fixFee =
drops(env.
current()->fees().increment);
1863 auto const ownerDirSle = env.
le(ownerDir);
1864 BEAST_EXPECT(ownerDirSle);
1865 BEAST_EXPECT(!ownerDirSle->isFieldPresent(sfExchangeRate));
1875 auto const bobOfferSeq{env.
seq(setup.bob)};
1876 env(
offer(setup.bob,
XRP(100), setup.usd(40)));
1882 auto const dirKey = sle->getFieldH256(sfBookDirectory);
1884 auto const dirSle = env.
le(
Keylet(ltDIR_NODE, dirKey));
1885 BEAST_EXPECT(dirSle);
1886 auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate);
1888 BEAST_EXPECT(exchangeRate == quality);
1900 Env env(*
this, features - fixCleanup3_2_0);
1901 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
1909 auto const aliceOfferSeq{env.
seq(alice_)};
1916 auto const openDirKey =
1917 sle->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory);
1919 auto const preCrossingQuality =
std::uint64_t{5623825668291712342ULL};
1920 auto const postCrossingQuality =
std::uint64_t{5623825668291712341ULL};
1924 auto const dirSle = env.
le(
Keylet(ltDIR_NODE, openDirKey));
1925 BEAST_EXPECT(dirSle);
1926 auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate);
1928 BEAST_EXPECT(exchangeRate == preCrossingQuality);
1929 BEAST_EXPECT(quality == postCrossingQuality);
1930 BEAST_EXPECT(exchangeRate != quality);
1937 auto const fixFee =
drops(env.
current()->fees().increment);
1943 auto const dirSle = env.
le(
Keylet(ltDIR_NODE, openDirKey));
1944 BEAST_EXPECT(dirSle);
1945 auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate);
1947 BEAST_EXPECT(exchangeRate == postCrossingQuality);
1948 BEAST_EXPECT(quality == postCrossingQuality);
1949 BEAST_EXPECT(exchangeRate == quality);
1962 bool const fixEnabled = features[fixCleanup3_2_0];
1964 testcase <<
"Cancel regular offer via domain OfferCreate"
1965 << (fixEnabled ?
" (fixCleanup3_2_0 enabled)" :
" (fixCleanup3_2_0 disabled)");
1976 Env env(*
this, features);
1977 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1980 auto const regularSeq = env.seq(bob);
1983 BEAST_EXPECT(
checkOffer(env, bob, regularSeq,
XRP(10), USD(10), 0,
false));
1985 auto const domainSeq = env.seq(bob);
1990 Json(jss::OfferSequence, regularSeq));
1993 BEAST_EXPECT(
checkOffer(env, bob, domainSeq,
XRP(20), USD(20), 0,
true));
1999 Json(jss::OfferSequence, regularSeq),
A generic endpoint for log messages.
TestcaseT testcase
Memberspace for declaring test cases.
Value removeMember(char const *key)
Remove and return the named member.
A currency issued by an account.
Writable ledger view that accumulates state and tx changes.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
void rawReplace(SLE::ref sle) override
Unconditionally replace a state item.
void testRippling(FeatureBitset features)
void testOfferCreate(FeatureBitset features)
void testHybridOfferDirectories(FeatureBitset features)
static std::optional< uint256 > getDefaultOfferDirKey(Env const &env, Account const &account, std::uint32_t offerSeq)
static uint256 getBookDirKey(Book const &book, STAmount const &takerPays, STAmount const &takerGets)
void testHybridOfferCreate(FeatureBitset features)
void testCancelRegularOfferWithDomainCreate(FeatureBitset features)
void testHybridOpenBookAfterCredentialExpiry(FeatureBitset features)
void testHybridOfferCrossingQuality(FeatureBitset features)
void testAutoBridge(FeatureBitset features)
void testOfferTokenIssuerInDomain(FeatureBitset features)
void run() override
Runs the suite.
void testPayment(FeatureBitset features)
static bool checkDirectorySize(Env const &env, uint256 directory, std::uint32_t dirSize)
void testHybridBookStep(FeatureBitset features)
void testAmmNotUsed(FeatureBitset features)
void testBookStep(FeatureBitset features)
static bool offerExists(Env const &env, Account const &account, std::uint32_t offerSeq)
void testAmmQualityNotLeaked(FeatureBitset features)
static bool checkOffer(Env const &env, Account const &account, std::uint32_t offerSeq, STAmount const &takerPays, STAmount const &takerGets, uint32_t const flags=0, bool const domainOffer=false)
void testBookExchangeRateFix(FeatureBitset features)
void testHybridMalformedOffer(FeatureBitset features)
void testHybridInvalidOffer(FeatureBitset features)
void testRemoveUnfundedOffer(FeatureBitset features)
Convenience class to test AMM functionality.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
SLE::const_pointer le(Account const &account) const
Return an account root.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
void enableFeature(uint256 const feature)
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
void require(Args const &... args)
Check a set of requirements.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Set Paths, SendMax on a JTx.
Sets the SendMax on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
T emplace_back(T... args)
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
Keylet book(Book const &b)
The beginning of an order book.
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
json::Value deleteCred(jtx::Account const &acc, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value bookExchangeRate(jtx::Account const &acct, uint256 const &bookDir)
Repair sfExchangeRate on a book directory's first page.
std::vector< Credential > Credentials
std::map< uint256, json::Value > getObjects(Account const &account, Env &env, bool withType)
json::Value setTx(AccountID const &account, Credentials const &credentials, std::optional< uint256 > domain)
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
json::Value offerCancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
XrpT const XRP
Converts to XRP Issue or STAmount.
std::uint32_t ownerCount(Env const &env, Account const &account)
FeatureBitset testableAmendments()
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
BaseUInt< 256 > Domain
Domain is a 256-bit hash representing a specific domain.
std::uint64_t getQuality(uint256 const &uBase)
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
A pair of SHAMap key and LedgerEntryType.