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/SeqProxy.h>
39#include <xrpl/protocol/TER.h>
40#include <xrpl/protocol/TxFlags.h>
41#include <xrpl/protocol/jss.h>
60 [[nodiscard]]
static bool
63 return static_cast<bool>(
67 [[nodiscard]]
static bool
74 uint32_t
const flags = 0,
75 bool const domainOffer =
false)
78 uint64_t
const pageIndex,
84 if (domain != (*page)[~sfDomainID])
87 auto const& indexes = page->getFieldV256(sfIndexes);
96 if (sle->getFieldAmount(sfTakerGets) != takerGets)
98 if (sle->getFieldAmount(sfTakerPays) != takerPays)
100 if (sle->getFlags() != flags)
102 if (domainOffer && !sle->isFieldPresent(sfDomainID))
104 if (!domainOffer && sle->isFieldPresent(sfDomainID))
107 sle->getFieldH256(sfBookDirectory),
108 sle->getFieldU64(sfBookNode),
109 (*sle)[~sfDomainID]))
112 if (sle->isFlag(lsfHybrid))
114 if (!sle->isFieldPresent(sfDomainID))
116 if (!sle->isFieldPresent(sfAdditionalBooks))
118 if (sle->getFieldArray(sfAdditionalBooks).size() != 1)
121 auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks);
123 for (
auto const& bookDir : additionalBookDirs)
125 auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory);
126 auto const& dirNode = bookDir.getFieldU64(sfBookNode);
130 if (!offerInDir(dirIndex, dirNode, std::nullopt))
136 if (sle->isFieldPresent(sfAdditionalBooks))
153 return Keylet(ltDIR_NODE, (*sle)[sfBookDirectory]).
key;
158 [[nodiscard]]
static bool
166 auto const page = env.
le(
171 pageIndex = (*page)[~sfIndexNext];
172 dirCnt += (*page)[sfIndexes].size();
174 }
while (pageIndex.
value_or(0) != 0u);
176 return dirCnt == dirSize;
186 Env env(*
this, features - featurePermissionedDEX);
187 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
203 if (features[fixCleanup3_2_0])
205 Env env(*
this, features);
206 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
215 Env env(*
this, features);
216 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
223 env.
trust(USD(1000), devin);
225 env(
pay(gw, devin, USD(100)));
248 Env env(*
this, features);
249 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
256 env.
trust(USD(1000), devin);
258 env(
pay(gw, devin, USD(100)));
262 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
263 jv[sfExpiration.jsonName] = t + 20;
283 Env env(*
this, features);
284 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
287 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
297 Env env(*
this, features);
298 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
304 auto const bobOfferSeq{env.
seq(bob)};
308 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
314 Env env(*
this, features);
315 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
321 auto const bobOfferSeq{env.
seq(bob)};
325 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq, USD(10),
XRP(10), 0,
true));
330 Env env(*
this, features);
331 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
334 auto const bobOfferSeq{env.
seq(bob)};
338 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
342 env(
offer(carol, USD(10),
XRP(10)));
345 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
347 auto const aliceOfferSeq{env.
seq(alice)};
351 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
358 Env env(*
this, features);
359 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
365 for (
size_t i = 0; i <= 100; i++)
367 auto const bobOfferSeq{env.
seq(bob)};
372 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
375 for (
auto const offerSeq : offerSeqs)
391 Env env(*
this, features - featurePermissionedDEX);
392 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
395 env(
pay(bob, alice, USD(10)),
416 if (features[fixCleanup3_2_0])
418 Env env(*
this, features);
419 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
422 env(
pay(bob_, alice_, USD(10)),
432 Env env(*
this, features);
433 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
436 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
439 env(
pay(bob, alice, USD(10)),
449 Env env(*
this, features);
450 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
460 env.
trust(USD(1000), devin);
462 env(
pay(gw, devin, USD(100)));
466 env(
pay(alice, devin, USD(10)),
478 env(
pay(alice, devin, USD(10)),
495 Env env(*
this, features);
496 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
506 env.
trust(USD(1000), devin);
508 env(
pay(gw, devin, USD(100)));
512 env(
pay(devin, alice, USD(10)),
524 env(
pay(devin, alice, USD(10)),
541 Env env(*
this, features);
542 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
568 Env env(*
this, features);
569 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
573 auto const regularOfferSeq{env.
seq(bob)};
576 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
579 BEAST_EXPECT(regularDirKey);
581 env, *regularDirKey, 1));
584 env(
pay(alice, carol, USD(10)),
592 auto const domainOfferSeq{env.
seq(bob)};
596 BEAST_EXPECT(
checkOffer(env, bob, domainOfferSeq,
XRP(10), USD(10), 0,
true));
599 BEAST_EXPECT(domainDirKey);
601 env, *domainDirKey, 1));
607 BEAST_EXPECT(!
offerExists(env, bob, domainOfferSeq));
608 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
612 env, *domainDirKey, 0));
614 env, *regularDirKey, 1));
619 Env env(*
this, features);
620 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
623 auto const eur = gw[
"EUR"];
624 env.
trust(eur(1000), alice);
626 env.
trust(eur(1000), bob);
628 env.
trust(eur(1000), carol);
630 env(
pay(gw, bob, eur(100)));
634 auto const usdOfferSeq{env.
seq(bob)};
638 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
641 env(
pay(alice, carol, eur(10)),
647 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
650 auto const regularOfferSeq{env.
seq(bob)};
651 env(
offer(bob, USD(10), eur(10)));
653 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq, USD(10), eur(10)));
657 env(
pay(alice, carol, eur(10)),
665 auto const eurOfferSeq{env.
seq(bob)};
666 env(
offer(bob, USD(10), eur(10)),
Domain(domainID));
668 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), 0,
true));
674 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(5), USD(5), 0,
true));
675 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), 0,
true));
686 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq, USD(10), eur(10)));
691 Env env(*
this, features);
692 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
696 Account const badDomainOwner(
"badDomainOwner");
698 env.
fund(
XRP(1000), badDomainOwner, devin);
700 env.
trust(USD(1000), devin);
702 env(
pay(gw, devin, USD(100)));
705 auto const badCredType =
"badCred";
707 {.issuer = badDomainOwner, .credType = badCredType}};
711 auto const badDomainID = objects.begin()->first;
722 env(
pay(alice, carol, USD(10)),
730 auto const bobOfferSeq{env.
seq(bob)};
733 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
746 Env env(*
this, features);
747 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
772 Env env(*
this, features);
773 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
780 env.
trust(USD(1000), devin);
782 env(
pay(gw, devin, USD(100)));
786 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
787 jv[sfExpiration.jsonName] = t + 20;
794 auto const offerSeq{env.
seq(devin)};
801 BEAST_EXPECT(
checkOffer(env, devin, offerSeq,
XRP(5), USD(5), 0,
true));
807 env(
pay(alice, carol, USD(5)),
813 BEAST_EXPECT(
checkOffer(env, devin, offerSeq,
XRP(5), USD(5), 0,
true));
818 Env env(*
this, features);
819 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
822 auto const offerSeq{env.
seq(bob)};
829 BEAST_EXPECT(
checkOffer(env, bob, offerSeq,
XRP(5), USD(5), 0,
true));
836 env(
pay(alice, carol, USD(5)),
842 BEAST_EXPECT(
checkOffer(env, bob, offerSeq,
XRP(5), USD(5), 0,
true));
854 Env env(*
this, features);
855 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
858 auto const eura = alice[
"EUR"];
859 auto const eurb = bob[
"EUR"];
861 env.
trust(eura(100), bob);
862 env.
trust(eurb(100), carol);
876 env(
trust(carol, bob[
"EUR"](0), bob, tfSetNoRipple));
888 testcase(
"Offer token issuer in domain");
892 Env env(*
this, features);
893 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
897 auto const bobOffer1Seq{env.
seq(bob)};
902 auto const bobOffer2Seq{env.
seq(bob)};
906 BEAST_EXPECT(
checkOffer(env, bob, bobOffer1Seq,
XRP(10), USD(10), 0,
true));
907 BEAST_EXPECT(
checkOffer(env, bob, bobOffer2Seq, USD(10),
XRP(10), lsfPassive,
true));
917 BEAST_EXPECT(!
offerExists(env, bob, bobOffer1Seq));
923 BEAST_EXPECT(!
offerExists(env, bob, bobOffer2Seq));
933 Env env(*
this, features);
934 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
937 auto const aliceOfferSeq{env.
seq(alice)};
941 auto const bobOfferSeq{env.
seq(bob)};
945 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(20), USD(20), 0,
true));
946 BEAST_EXPECT(
checkOffer(env, alice, aliceOfferSeq,
XRP(100), USD(100), 0,
true));
949 BEAST_EXPECT(domainDirKey);
951 env, *domainDirKey, 2));
960 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
963 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
965 env, *domainDirKey, 1));
973 Env env(*
this, features);
974 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
976 AMM const amm(env, alice,
XRP(10), USD(50));
979 env(
pay(bob, carol, USD(5)),
991 auto [xrp, usd, lpt] =
amm.balances(
XRP, USD);
992 BEAST_EXPECT(usd == USD(45));
998 bool const excludesAmmFromDomainQuality = features[fixCleanup3_3_0];
1000 testcase <<
"AMM quality not leaked into domain BookStep"
1001 << (excludesAmmFromDomainQuality ?
" (Cleanup3_3_0 enabled)"
1002 :
" (Cleanup3_3_0 disabled)");
1004 Env env(*
this, features);
1005 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1007 auto const eur = gw[
"EUR"];
1009 env.trust(eur(1000), bob, domainOwner);
1011 env(
pay(gw, bob, eur(100)));
1014 env(
pay(gw, alice, USD(500)));
1020 AMM const amm(env, alice,
XRP(10), USD(500));
1022 auto const directOfferSeq{env.seq(bob)};
1026 auto const xrpEurOfferSeq{env.seq(bob)};
1030 auto const eurUsdOfferSeq{env.seq(domainOwner)};
1031 env(
offer(domainOwner, eur(20), USD(20)),
Domain(domainID));
1034 auto const carolBalBefore = env.balance(carol, USD);
1041 env(
pay(alice, carol, USD(100)),
1045 Txflags(tfPartialPayment | tfNoRippleDirect),
1049 auto const delivered = env.balance(carol, USD) - carolBalBefore;
1050 if (excludesAmmFromDomainQuality)
1052 BEAST_EXPECT(delivered == USD(20));
1054 BEAST_EXPECT(
checkOffer(env, bob, directOfferSeq,
XRP(10), USD(10), 0,
true));
1055 BEAST_EXPECT(!
offerExists(env, bob, xrpEurOfferSeq));
1056 BEAST_EXPECT(!
offerExists(env, domainOwner, eurUsdOfferSeq));
1060 BEAST_EXPECT(delivered == USD(10));
1062 BEAST_EXPECT(!
offerExists(env, bob, directOfferSeq));
1063 BEAST_EXPECT(
checkOffer(env, bob, xrpEurOfferSeq,
XRP(10), eur(20), 0,
true));
1064 BEAST_EXPECT(
checkOffer(env, domainOwner, eurUsdOfferSeq, eur(20), USD(20), 0,
true));
1067 auto [xrp, usd, lpt] =
amm.balances(
XRP, USD);
1068 BEAST_EXPECT(xrp ==
XRP(10));
1069 BEAST_EXPECT(usd == USD(500));
1079 Env env(*
this, features - featurePermissionedDEX);
1080 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1100 auto const offerSeq{env.
seq(bob)};
1103 BEAST_EXPECT(
checkOffer(env, bob, offerSeq,
XRP(10), USD(10), lsfHybrid,
true));
1108 Env env(*
this, features);
1109 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1112 auto const bobOfferSeq{env.
seq(bob)};
1116 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1120 auto const aliceOfferSeq{env.
seq(alice)};
1124 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
1125 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1131 Env env(*
this, features);
1132 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1135 auto const bobOfferSeq{env.
seq(bob)};
1141 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1143 auto const aliceOfferSeq{env.
seq(alice)};
1144 env(
offer(alice, USD(10),
XRP(10)));
1147 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
1148 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1155 Env env(*
this, features);
1156 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1159 auto const bobOfferSeq{env.
seq(bob)};
1163 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
true));
1167 auto const aliceOfferSeq{env.
seq(alice)};
1171 BEAST_EXPECT(!
offerExists(env, alice, aliceOfferSeq));
1172 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1179 Env env(*
this, features);
1180 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1183 auto const bobOfferSeq{env.
seq(bob)};
1187 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
false));
1191 auto const aliceOfferSeq{env.
seq(alice)};
1195 BEAST_EXPECT(
offerExists(env, alice, aliceOfferSeq));
1197 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), 0,
false));
1198 BEAST_EXPECT(
checkOffer(env, alice, aliceOfferSeq, USD(10),
XRP(10), lsfHybrid,
true));
1211 Env env(*
this, features);
1212 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1215 auto const hybridOfferSeq{env.
seq(bob)};
1225 env(
pay(alice, carol, USD(5)),
1231 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(50), USD(50), lsfHybrid,
true));
1233 if (features[fixCleanup3_3_0])
1237 auto const carolBalBefore = env.
balance(carol, USD);
1240 BEAST_EXPECT(env.
balance(carol, USD) - carolBalBefore == USD(5));
1241 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(45), USD(45), lsfHybrid,
true));
1244 auto const regularOfferSeq{env.
seq(bob)};
1247 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(10), USD(10)));
1249 auto const sleHybridOffer =
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 =
1285 if (!BEAST_EXPECT(sleHybridOffer))
1287 auto const openDir =
1288 sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory);
1297 BEAST_EXPECT(!
offerExists(env, bob, hybridOfferSeq));
1298 BEAST_EXPECT(
checkOffer(env, bob, regularOfferSeq,
XRP(5), USD(5)));
1310 Env env(*
this, features);
1311 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1314 auto const hybridOfferSeq{env.
seq(bob)};
1320 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(5), USD(5), lsfHybrid,
true));
1326 BEAST_EXPECT(!
offerExists(env, bob, hybridOfferSeq));
1332 Env env(*
this, features);
1333 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1337 Account const badDomainOwner(
"badDomainOwner");
1339 env.
fund(
XRP(1000), badDomainOwner, devin);
1342 auto const badCredType =
"badCred";
1344 {.issuer = badDomainOwner, .credType = badCredType}};
1348 auto const badDomainID = objects.begin()->first;
1355 auto const hybridOfferSeq{env.
seq(bob)};
1360 env(
pay(devin, badDomainOwner, USD(5)),
1366 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1370 BEAST_EXPECT(
checkOffer(env, bob, hybridOfferSeq,
XRP(5), USD(5), lsfHybrid,
true));
1376 BEAST_EXPECT(!
offerExists(env, bob, hybridOfferSeq));
1381 Env env(*
this, features);
1382 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1385 auto const eur = gw[
"EUR"];
1386 env.
trust(eur(1000), alice);
1388 env.
trust(eur(1000), bob);
1390 env.
trust(eur(1000), carol);
1392 env(
pay(gw, bob, eur(100)));
1395 auto const usdOfferSeq{env.
seq(bob)};
1399 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
1402 env(
pay(alice, carol, eur(5)),
1408 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
true));
1411 auto const eurOfferSeq{env.
seq(bob)};
1414 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), lsfHybrid,
true));
1420 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(5), USD(5), 0,
true));
1421 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), lsfHybrid,
true));
1426 Env env(*
this, features);
1427 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1430 auto const eur = gw[
"EUR"];
1431 env.
trust(eur(1000), alice);
1433 env.
trust(eur(1000), bob);
1435 env.
trust(eur(1000), carol);
1437 env(
pay(gw, bob, eur(100)));
1441 auto const usdOfferSeq{env.
seq(bob)};
1445 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(10), USD(10), 0,
false));
1448 auto const eurOfferSeq{env.
seq(bob)};
1451 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), lsfHybrid,
true));
1457 BEAST_EXPECT(
checkOffer(env, bob, usdOfferSeq,
XRP(5), USD(5), 0,
false));
1458 BEAST_EXPECT(
checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), lsfHybrid,
true));
1469 testcase(
"Hybrid open book after credential expiry");
1471 Env env(*
this, features);
1472 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1478 env.
trust(USD(1000), devin);
1480 env(
pay(gw, devin, USD(100)));
1486 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
1487 jv[sfExpiration.jsonName] = t + 100;
1495 auto const hybridOfferSeq{env.
seq(devin)};
1499 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1503 auto carolBalance = env.
balance(carol, USD);
1506 BEAST_EXPECT(env.
balance(carol, USD) - carolBalance == USD(5));
1507 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(5), USD(5), lsfHybrid,
true));
1517 BEAST_EXPECT(
offerExists(env, devin, hybridOfferSeq));
1521 carolBalance = env.
balance(carol, USD);
1526 BEAST_EXPECT(env.
balance(carol, USD) - carolBalance == USD(2));
1528 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(3), USD(3), lsfHybrid,
true));
1533 env(
pay(alice, carol, USD(1)),
1542 BEAST_EXPECT(
checkOffer(env, devin, hybridOfferSeq,
XRP(3), USD(3), lsfHybrid,
true));
1545 carolBalance = env.
balance(carol, USD);
1548 BEAST_EXPECT(env.
balance(carol, USD) - carolBalance == USD(3));
1549 BEAST_EXPECT(!
offerExists(env, devin, hybridOfferSeq));
1555 Env env(*
this, features);
1556 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1568 size_t dirCnt = 100;
1570 for (
size_t i = 1; i <= dirCnt; i++)
1572 auto const bobOfferSeq{env.
seq(bob)};
1577 auto const sleOffer =
1579 BEAST_EXPECT(sleOffer);
1580 BEAST_EXPECT(sleOffer->getFieldH256(sfBookDirectory) == domainDir);
1581 BEAST_EXPECT(sleOffer->getFieldArray(sfAdditionalBooks).size() == 1);
1583 sleOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory) ==
1586 BEAST_EXPECT(
checkOffer(env, bob, bobOfferSeq,
XRP(10), USD(10), lsfHybrid,
true));
1591 for (
auto const offerSeq : offerSeqs)
1607 Env env(*
this, features);
1608 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1610 auto const eur = gw[
"EUR"];
1612 for (
auto const& account : {alice, bob, carol})
1614 env(
trust(account, eur(10000)));
1618 env(
pay(gw, carol, eur(1)));
1621 auto const aliceOfferSeq{env.
seq(alice)};
1622 auto const bobOfferSeq{env.
seq(bob)};
1629 auto const carolOfferSeq{env.
seq(carol)};
1630 env(
offer(carol, USD(1), eur(1)),
Domain(domainID));
1633 BEAST_EXPECT(!
offerExists(env, bob, aliceOfferSeq));
1634 BEAST_EXPECT(!
offerExists(env, bob, bobOfferSeq));
1635 BEAST_EXPECT(!
offerExists(env, bob, carolOfferSeq));
1641 bool const fixEnabled = features[fixCleanup3_1_3];
1643 testcase <<
"Hybrid offer with empty AdditionalBooks"
1644 << (fixEnabled ?
" (fixCleanup3_1_3 enabled)" :
" (fixCleanup3_1_3 disabled)");
1661 Env env(*
this, features);
1662 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1666 auto const bobOfferSeq{env.seq(bob)};
1676 auto const sle = view.
read(offerKey);
1680 replacement->setFieldArray(sfAdditionalBooks,
STArray{});
1689 env(
pay(alice, carol, USD(10)),
1707 bool const fixEnabled = features[fixCleanup3_2_0];
1708 testcase <<
"Hybrid offer crossing quality"
1709 << (fixEnabled ?
" (fixCleanup3_2_0)" :
" (pre-fix)");
1724 Env env(*
this, features);
1725 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
1729 auto const bobOfferSeq{env.seq(bob_)};
1732 BEAST_EXPECT(
offerExists(env, bob_, bobOfferSeq));
1738 auto const aliceOfferSeq{env.seq(alice_)};
1745 BEAST_EXPECT(sle->isFieldPresent(sfAdditionalBooks));
1746 BEAST_EXPECT(sle->getFieldArray(sfAdditionalBooks).size() == 1);
1748 auto const domainDirKey = sle->getFieldH256(sfBookDirectory);
1749 auto const openDirKey =
1750 sle->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory);
1752 auto const domainQuality =
getQuality(domainDirKey);
1753 auto const openQuality =
getQuality(openDirKey);
1756 auto const domainDirSle = env.le(
Keylet(ltDIR_NODE, domainDirKey));
1757 auto const openDirSle = env.le(
Keylet(ltDIR_NODE, openDirKey));
1758 BEAST_EXPECT(domainDirSle);
1759 BEAST_EXPECT(openDirSle);
1761 auto const domainExRate = domainDirSle->getFieldU64(sfExchangeRate);
1762 auto const openExRate = openDirSle->getFieldU64(sfExchangeRate);
1763 auto const preCrossingQuality =
std::uint64_t{5623825668291712342ULL};
1764 auto const postCrossingQuality =
std::uint64_t{5623825668291712341ULL};
1768 BEAST_EXPECT(domainQuality == preCrossingQuality);
1769 BEAST_EXPECT(domainExRate == preCrossingQuality);
1770 BEAST_EXPECT(domainExRate == domainQuality);
1775 BEAST_EXPECT(openQuality == preCrossingQuality);
1776 BEAST_EXPECT(domainQuality == openQuality);
1779 BEAST_EXPECT(openExRate == preCrossingQuality);
1780 BEAST_EXPECT(openExRate == openQuality);
1786 BEAST_EXPECT(openQuality == postCrossingQuality);
1787 BEAST_EXPECT(domainQuality != openQuality);
1792 BEAST_EXPECT(openExRate == preCrossingQuality);
1793 BEAST_EXPECT(openExRate != openQuality);
1794 BEAST_EXPECT(openExRate == domainQuality);
1801 testcase(
"LedgerStateFix BookExchangeRate");
1818 Env env(*
this, features - fixCleanup3_2_0);
1830 Env env(*
this, features);
1838 missingBookDirectory.
removeMember(sfBookDirectory.jsonName);
1844 extraOwner[sfOwner.jsonName] = carol.
human();
1849 Env env(*
this, features);
1851 auto const fixFee =
drops(env.
current()->fees().increment);
1865 auto const ownerDirSle = env.
le(ownerDir);
1866 BEAST_EXPECT(ownerDirSle);
1867 BEAST_EXPECT(!ownerDirSle->isFieldPresent(sfExchangeRate));
1877 auto const bobOfferSeq{env.
seq(setup.bob)};
1878 env(
offer(setup.bob,
XRP(100), setup.usd(40)));
1885 auto const dirKey = sle->getFieldH256(sfBookDirectory);
1887 auto const dirSle = env.
le(
Keylet(ltDIR_NODE, dirKey));
1888 BEAST_EXPECT(dirSle);
1889 auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate);
1891 BEAST_EXPECT(exchangeRate == quality);
1903 Env env(*
this, features - fixCleanup3_2_0);
1904 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] =
1912 auto const aliceOfferSeq{env.
seq(alice_)};
1920 auto const openDirKey =
1921 sle->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory);
1923 auto const preCrossingQuality =
std::uint64_t{5623825668291712342ULL};
1924 auto const postCrossingQuality =
std::uint64_t{5623825668291712341ULL};
1928 auto const dirSle = env.
le(
Keylet(ltDIR_NODE, openDirKey));
1929 BEAST_EXPECT(dirSle);
1930 auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate);
1932 BEAST_EXPECT(exchangeRate == preCrossingQuality);
1933 BEAST_EXPECT(quality == postCrossingQuality);
1934 BEAST_EXPECT(exchangeRate != quality);
1941 auto const fixFee =
drops(env.
current()->fees().increment);
1947 auto const dirSle = env.
le(
Keylet(ltDIR_NODE, openDirKey));
1948 BEAST_EXPECT(dirSle);
1949 auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate);
1951 BEAST_EXPECT(exchangeRate == postCrossingQuality);
1952 BEAST_EXPECT(quality == postCrossingQuality);
1953 BEAST_EXPECT(exchangeRate == quality);
1966 bool const fixEnabled = features[fixCleanup3_2_0];
1968 testcase <<
"Cancel regular offer via domain OfferCreate"
1969 << (fixEnabled ?
" (fixCleanup3_2_0 enabled)" :
" (fixCleanup3_2_0 disabled)");
1980 Env env(*
this, features);
1981 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
1984 auto const regularSeq = env.seq(bob);
1987 BEAST_EXPECT(
checkOffer(env, bob, regularSeq,
XRP(10), USD(10), 0,
false));
1989 auto const domainSeq = env.seq(bob);
1994 Json(jss::OfferSequence, regularSeq));
1997 BEAST_EXPECT(
checkOffer(env, bob, domainSeq,
XRP(20), USD(20), 0,
true));
2003 Json(jss::OfferSequence, regularSeq),
2014 bool const fixEnabled = features[fixCleanup3_4_0];
2016 testcase <<
"Replace domain offer via OfferCreate"
2017 << (fixEnabled ?
" (fixCleanup3_4_0 enabled)" :
" (fixCleanup3_4_0 disabled)");
2019 Env env(*
this, features);
2020 auto const& [gw, domainOwner, alice, bob, carol, USD, domainA, credType] =
2023 Account const domainOwnerB(
"permdex-domainOwnerB");
2024 auto const domainB =
2025 setupDomain(env, {alice, bob, carol, gw}, domainOwnerB,
"permdex-other-domain");
2026 BEAST_EXPECT(domainA != domainB);
2028 auto const oldSeq = env.seq(alice);
2032 BEAST_EXPECT(
checkOffer(env, alice, oldSeq, USD(100),
XRP(1), 0,
true));
2034 if (!BEAST_EXPECT(oldOffer))
2036 BEAST_EXPECT(oldOffer->getFieldH256(sfDomainID) == domainA);
2038 auto const newSeq = env.seq(alice);
2043 env(
offer(alice, USD(100),
XRP(2)),
Domain(domainB),
Json(jss::OfferSequence, oldSeq));
2047 BEAST_EXPECT(
checkOffer(env, alice, newSeq, USD(100),
XRP(2), 0,
true));
2049 if (!BEAST_EXPECT(newOffer))
2051 BEAST_EXPECT(newOffer->getFieldH256(sfDomainID) == domainB);
2055 env(
offer(alice, USD(100),
XRP(2)),
2057 Json(jss::OfferSequence, oldSeq),
2061 BEAST_EXPECT(
checkOffer(env, alice, oldSeq, USD(100),
XRP(1), 0,
true));
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.
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
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)
void testReplaceDomainOfferWithOtherDomainOffer(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 const q) noexcept
The initial directory page for a specific quality.
Keylet offer(AccountID const &id, SeqProxy const &seq) noexcept
An offer from an account.
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 page(uint256 const &root, std::uint64_t const 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()
uint256 setupDomain(jtx::Env &env, std::vector< jtx::Account > const &accounts, jtx::Account const &domainOwner, std::string const &credType)
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.