1#include <test/jtx/AMM.h>
2#include <test/jtx/CaptureLogs.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/PathSet.h>
5#include <test/jtx/TestHelpers.h>
6#include <test/jtx/WSClient.h>
7#include <test/jtx/acctdelete.h>
8#include <test/jtx/amount.h>
9#include <test/jtx/balance.h>
10#include <test/jtx/envconfig.h>
11#include <test/jtx/fee.h>
12#include <test/jtx/jtx_json.h>
13#include <test/jtx/mpt.h>
14#include <test/jtx/noop.h>
15#include <test/jtx/offer.h>
16#include <test/jtx/owners.h>
17#include <test/jtx/paths.h>
18#include <test/jtx/pay.h>
19#include <test/jtx/require.h>
20#include <test/jtx/sendmax.h>
21#include <test/jtx/tags.h>
22#include <test/jtx/ter.h>
23#include <test/jtx/ticket.h>
24#include <test/jtx/trust.h>
25#include <test/jtx/txflags.h>
27#include <xrpl/basics/base_uint.h>
28#include <xrpl/beast/unit_test/suite.h>
29#include <xrpl/beast/utility/Journal.h>
30#include <xrpl/core/ServiceRegistry.h>
31#include <xrpl/json/json_value.h>
32#include <xrpl/ledger/helpers/DirectoryHelpers.h>
33#include <xrpl/protocol/AccountID.h>
34#include <xrpl/protocol/Book.h>
35#include <xrpl/protocol/Feature.h>
36#include <xrpl/protocol/Indexes.h>
37#include <xrpl/protocol/Issue.h>
38#include <xrpl/protocol/LedgerFormats.h>
39#include <xrpl/protocol/MPTIssue.h>
40#include <xrpl/protocol/Protocol.h>
41#include <xrpl/protocol/SField.h>
42#include <xrpl/protocol/STAmount.h>
43#include <xrpl/protocol/Seed.h>
44#include <xrpl/protocol/SeqProxy.h>
45#include <xrpl/protocol/TER.h>
46#include <xrpl/protocol/TxFlags.h>
47#include <xrpl/protocol/UintTypes.h>
48#include <xrpl/protocol/XRPAmount.h>
49#include <xrpl/protocol/jss.h>
70 return env.
current()->fees().accountReserve(count, 1);
76 return env.
current()->header().parentCloseTime.time_since_epoch().count();
83 testcase(
"Incorrect Removal of Funded Offers");
95 auto const gw =
Account{
"gateway"};
100 auto test = [&](
auto&& issue1,
auto&& issue2) {
101 Env env{*
this, features};
103 env.
fund(
XRP(10'000), alice, bob, carol, gw);
107 issue1({.env = env, .token =
"USD", .issuer = gw, .holders = {alice, bob, carol}});
109 issue2({.env = env, .token =
"BTC", .issuer = gw, .holders = {alice, bob, carol}});
111 env(
pay(gw, alice, btc(1'000)));
113 env(
pay(gw, carol, usd(1'000)));
114 env(
pay(gw, carol, btc(1'000)));
119 env(
offer(carol, btc(49),
XRP(49)));
120 env(
offer(carol, btc(51),
XRP(51)));
124 env(
offer(carol,
XRP(50), usd(50)));
125 env(
offer(carol,
XRP(50), usd(50)));
128 env(
offer(carol, btc(1), usd(100)));
132 env(
pay(alice, bob, usd(100)),
139 !
isOffer(env, carol, btc(1), usd(100)) &&
isOffer(env, carol, btc(49),
XRP(49)));
147 testcase(
"Removing Canceled Offers");
150 Env env{*
this, features};
152 auto const gw =
Account{
"gateway"};
153 auto const alice =
Account{
"alice"};
155 env.
fund(
XRP(10'000), alice, gw);
158 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
160 env(
pay(gw, alice, usd(50)));
163 auto const offer1Seq = env.
seq(alice);
168 BEAST_EXPECT(
isOffer(env, alice,
XRP(500), usd(100)));
171 auto const offer2Seq = env.
seq(alice);
173 env(
offer(alice,
XRP(300), usd(100)),
174 Json(jss::OfferSequence, offer1Seq),
184 env(
offer(alice,
XRP(400), usd(200)),
185 Json(jss::OfferSequence, offer1Seq),
193 auto const offer4Seq = env.
seq(alice);
197 BEAST_EXPECT(
isOffer(env, alice,
XRP(222), usd(111)));
200 BEAST_EXPECT(env.
seq(alice) == offer4Seq + 2);
202 BEAST_EXPECT(!
isOffer(env, alice,
XRP(222), usd(111)));
210 Json(jss::OfferSequence, offer2Seq),
215 BEAST_EXPECT(
isOffer(env, alice,
XRP(300), usd(100)));
216 BEAST_EXPECT(!
isOffer(env, alice,
XRP(5), usd(2)));
226 using namespace std::chrono_literals;
227 auto const alice =
Account{
"alice"};
228 auto const bob =
Account{
"bob"};
229 auto const carol =
Account{
"carol"};
232 auto test = [&](
auto&& issue1,
auto&& issue2) {
233 Env env{*
this, features};
235 env.
fund(
XRP(10'000), alice, bob, carol, gw);
238 auto const usd = issue1(
242 .holders = {alice, bob, carol},
243 .
limit = 400'000'000});
244 auto const eur = issue2(
248 .holders = {alice, bob, carol},
249 .
limit = 400'000'000});
251 env(
pay(gw, alice, usd(100'000'000)));
252 env(
pay(gw, carol, eur(100'000'000)));
258 for (
int i = 0; i < 101; ++i)
259 env(
offer(carol, usd(1'000'000), eur(2'000'000)));
290 using namespace std::chrono_literals;
291 auto const alice =
Account{
"alice"};
292 auto const bob =
Account{
"bob"};
293 auto const carol =
Account{
"carol"};
294 auto const dan =
Account{
"dan"};
295 auto const erin =
Account{
"erin"};
298 Env env{*
this, features};
300 env.
fund(
XRP(10'000), alice, bob, carol, dan, erin, gw);
306 .holders = {alice, bob, carol, dan, erin},
307 .pay = std::nullopt});
308 env(
pay(gw, carol, usd(99'999)));
309 env(
pay(gw, dan, usd(100'000)));
310 env(
pay(gw, erin, usd(100'000)));
332 env(
pay(alice, bob, usd(100'000)),
335 Txflags(tfNoRippleDirect | tfPartialPayment));
347 testcase(
"Rm small increased q offers XRP");
355 using namespace std::chrono_literals;
356 auto const alice =
Account{
"alice"};
357 auto const bob =
Account{
"bob"};
358 auto const carol =
Account{
"carol"};
362 for (
auto crossBothOffers : {
false,
true})
364 Env env{*
this, features};
366 env.
fund(
XRP(10'000), alice, bob, carol, gw);
368 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob, carol}});
370 auto initialCarolUSD = usd(499);
371 env(
pay(gw, carol, initialCarolUSD));
372 env(
pay(gw, bob, usd(100'000)));
378 env(
offer(bob,
drops(2), usd(1'000), tfPassive));
384 auto aliceTakerGets = crossBothOffers ?
drops(2) :
drops(1);
385 env(
offer(alice, usd(1'000), aliceTakerGets));
406 for (
auto partialPayment : {
false,
true})
408 Env env{*
this, features};
410 env.
fund(
XRP(10'000), alice, bob, carol, gw);
413 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob, carol}});
414 auto const initialCarolUSD = usd(999);
415 env(
pay(gw, carol, initialCarolUSD));
417 env(
pay(gw, bob, usd(100'000)));
421 env(
offer(bob,
drops(2), usd(2'000), tfPassive));
426 partialPayment ? (tfNoRippleDirect | tfPartialPayment) : tfNoRippleDirect;
430 env(
pay(alice, bob, usd(5'000)),
456 testcase(
"Rm small increased q offers MPT");
464 using namespace std::chrono_literals;
465 auto const alice =
Account{
"alice"};
466 auto const bob =
Account{
"bob"};
467 auto const carol =
Account{
"carol"};
470 auto test = [&](
auto&& issue1,
auto&& issue2) {
491 for (
auto crossBothOffers : {
false,
true})
493 Env env{*
this, features};
495 env.
fund(
XRP(10'000), alice, bob, carol, gw);
498 auto const usd = issue1(
502 .holders = {alice, bob, carol},
503 .
limit = 100'000'000});
504 auto const eur = issue2(
508 .holders = {alice, bob, carol},
509 .
limit = 100'000'000});
511 auto initialCarolUSD = tinyAmount(usd);
512 env(
pay(gw, carol, initialCarolUSD));
513 env(
pay(gw, bob, usd(100'000)));
514 env(
pay(gw, alice, eur(100'000)));
517 env(
offer(carol, eur(10), usd(10'000)));
520 env(
offer(bob, eur(10), usd(5'000), tfPassive));
527 auto aliceTakerGets = crossBothOffers ? eur(2) : eur(1);
528 env(
offer(alice, usd(1'000), aliceTakerGets));
536 auto const balanceCarolUSD = kIsEuriou ? usd(0) : initialCarolUSD;
547 auto const balanceAliceUSD = kIsEuriou ? usd(1) : usd(0);
553 for (
auto partialPayment : {
false,
true})
555 Env env{*
this, features};
557 env.
fund(
XRP(10'000), alice, bob, carol, gw);
560 auto const usd = issue1(
564 .holders = {alice, bob, carol},
565 .
limit = 100'000'000});
566 auto const eur = issue2(
570 .holders = {alice, bob, carol},
571 .
limit = 100'000'000});
573 auto const initialCarolUSD = tinyAmount(usd);
574 env(
pay(gw, carol, initialCarolUSD));
575 env(
pay(gw, bob, usd(100'000)));
576 env(
pay(gw, alice, eur(100'000)));
579 env(
offer(carol, eur(10), usd(2'000)));
581 env(
offer(bob, eur(20), usd(4'000), tfPassive));
586 partialPayment ? (tfNoRippleDirect | tfPartialPayment) : tfNoRippleDirect;
590 env(
pay(alice, bob, usd(5'000)),
604 auto const balanceCarolUSD = kIsEuriou ? usd(0) : initialCarolUSD;
623 testcase(
"MPT issuer offer dust removal uses remaining issuance capacity");
627 Account const issuer{
"issuer"};
631 Env env{*
this, features};
632 env.
fund(
XRP(10'000), issuer, carol, bob);
636 {.env = env, .issuer = issuer, .holders = {carol, bob}, .maxAmt = 101});
641 auto const issuerOfferSeq = env.
seq(issuer);
645 env(
pay(issuer, carol, musd(100)));
647 BEAST_EXPECT(env.
balance(issuer, musd) == musd(-100));
648 BEAST_EXPECT(env.
balance(carol, musd) == musd(100));
654 auto const carolOfferSeq = env.
seq(carol);
660 BEAST_EXPECT(env.
le(issuerOffer) !=
nullptr);
661 BEAST_EXPECT(env.
le(carolOffer) !=
nullptr);
663 env(
offer(bob, musd(101),
drops(2), tfImmediateOrCancel));
666 BEAST_EXPECT(env.
le(issuerOffer) ==
nullptr);
667 BEAST_EXPECT(env.
le(carolOffer) ==
nullptr);
669 BEAST_EXPECT(env.
balance(issuer, musd) == musd(-100));
670 BEAST_EXPECT(env.
balance(carol, musd) == musd(0));
671 BEAST_EXPECT(env.
balance(bob, musd) == musd(100));
678 auto const alice =
Account{
"alice"};
679 auto const bob =
Account{
"bob"};
682 testcase(
"Partially funded MPT/XRP input offer cannot be consumed for free");
684 Env env{*
this, features};
687 env.
fund(
XRP(10'000), gw, alice, bob);
690 MPTTester const usd({.env = env, .issuer = gw, .holders = {alice}});
692 auto const aliceOfferSeq = env.
seq(alice);
696 auto const targetBalance =
reserve(env, 2) +
drops(999'999);
702 auto const aliceXRPBefore = env.
balance(alice);
703 auto const bobXRPBefore = env.
balance(bob);
708 Txflags(tfNoRippleDirect | tfPartialPayment),
723 BEAST_EXPECT(env.
balance(alice) == aliceXRPBefore);
724 BEAST_EXPECT(env.
balance(bob) == bobXRPBefore);
728 testcase(
"Partially funded MPT/IOU input offer cannot be consumed for free");
730 Env env{*
this, features};
731 auto const mptIssuer =
Account{
"mptIssuer"};
732 auto const iouIssuer =
Account{
"iouIssuer"};
734 env.
fund(
XRP(10'000), mptIssuer, iouIssuer, alice, bob);
737 auto const eur = iouIssuer[
"EUR"];
738 env.
trust(eur(100), alice, bob);
739 env(
pay(iouIssuer, alice, eur(0.5)));
742 MPTTester const usd({.env = env, .issuer = mptIssuer, .holders = {alice}});
744 auto const aliceOfferSeq = env.
seq(alice);
745 env(
offer(alice, usd(1), eur(1)));
748 auto const aliceEURBefore = env.
balance(alice, eur);
749 auto const bobEURBefore = env.
balance(bob, eur);
751 env(
pay(mptIssuer, bob, eur(1)),
754 Txflags(tfNoRippleDirect | tfPartialPayment),
764 BEAST_EXPECT(env.
balance(alice, eur) == aliceEURBefore);
765 BEAST_EXPECT(env.
balance(bob, eur) == bobEURBefore);
769 testcase(
"Partially funded MPT/MPT input offer cannot be consumed for free");
771 Env env{*
this, features};
772 auto const issuerA =
Account{
"issuerA"};
773 auto const issuerB =
Account{
"issuerB"};
775 env.
fund(
XRP(10'000), issuerA, issuerB, alice, bob);
778 MPTTester const usd({.env = env, .issuer = issuerA, .holders = {alice}});
779 MPTTester const eur({.env = env, .issuer = issuerB, .holders = {alice, bob}});
781 env(
pay(issuerB, alice, eur(999'999)));
784 auto const aliceOfferSeq = env.
seq(alice);
785 env(
offer(alice, usd(1), eur(1'000'000)));
788 auto const aliceEURBefore = eur.getBalance(alice);
789 auto const bobEURBefore = eur.getBalance(bob);
791 env(
pay(issuerA, bob, eur(1'000'000)),
794 Txflags(tfNoRippleDirect | tfPartialPayment),
804 BEAST_EXPECT(env.
balance(alice, eur) == eur(aliceEURBefore));
805 BEAST_EXPECT(env.
balance(bob, eur) == eur(bobEURBefore));
815 "Partially funded MPT input offer is removed, not consumed, "
816 "when a funded offer crosses");
818 Env env{*
this, features};
820 auto const carol =
Account{
"carol"};
822 env.
fund(
XRP(10'000), gw, alice, carol, bob);
825 MPTTester const usd({.env = env, .issuer = gw, .holders = {alice, carol, bob}});
831 auto const aliceOfferSeq = env.
seq(alice);
835 auto const targetBalance =
reserve(env, 2) +
drops(999'999);
843 auto const carolOfferSeq = env.
seq(carol);
848 env(
pay(gw, bob, usd(2)));
853 BEAST_EXPECT(env.
le(aliceOffer) !=
nullptr);
854 BEAST_EXPECT(env.
le(carolOffer) !=
nullptr);
856 auto const aliceXRPBefore = env.
balance(alice);
857 auto const bobXRPBefore = env.
balance(bob);
863 env(
offer(bob,
drops(2'000'000), usd(2), tfImmediateOrCancel));
866 BEAST_EXPECT(env.
le(aliceOffer) ==
nullptr);
867 BEAST_EXPECT(env.
le(carolOffer) ==
nullptr);
872 BEAST_EXPECT(env.
balance(alice) == aliceXRPBefore);
873 BEAST_EXPECT(env.
balance(alice, usd) == usd(0));
874 BEAST_EXPECT(env.
balance(carol, usd) == usd(1));
875 BEAST_EXPECT(env.
balance(bob, usd) == usd(1));
894 auto const gw =
Account{
"gateway"};
895 auto const alice =
Account{
"alice"};
896 auto const bob =
Account{
"bob"};
897 auto const carol =
Account{
"carol"};
899 auto const xrpOffer =
XRP(1'000);
903 Env env{*
this, features};
907 auto const f = env.
current()->fees().base;
908 auto const r =
reserve(env, 0);
910 env.
fund(r + f, alice);
912 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
914 auto const usdOffer = usd(1'000);
924 Env env{*
this, features};
928 auto const f = env.
current()->fees().base;
929 auto const r =
reserve(env, 0);
931 env.
fund(r + f, alice);
932 env.
fund(r + 2 * f + xrpOffer, bob);
934 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
936 auto const usdOffer = usd(1'000);
937 auto const usdOffer2 = usd(500);
938 auto const xrpOffer2 =
XRP(500);
946 Balance(alice, r - f + xrpOffer2),
958 Env env{*
this, features};
962 auto const f = env.
current()->fees().base;
963 auto const r =
reserve(env, 0);
965 env.
fund(r + f, alice);
967 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
969 auto const usdOffer = usd(1'000);
970 auto const usdOffer2 = usd(500);
971 auto const xrpOffer2 =
XRP(500);
973 env.
fund(r + f + xrpOffer, bob, carol);
981 Balance(alice, r - f + xrpOffer),
999 if (sle->getType() == ltOFFER)
1000 result.push_back(sle);
1010 using namespace jtx;
1012 auto const startBalance =
XRP(1'000'000);
1013 auto const gw =
Account{
"gateway"};
1014 auto const alice =
Account{
"alice"};
1015 auto const bob =
Account{
"bob"};
1022 Env env{*
this, features};
1024 auto const f = env.
current()->fees().base;
1026 env.
fund(startBalance, gw, alice, bob);
1028 MPTTester musd({.env = env, .issuer = gw});
1029 MPT const usd = musd[
"USD"];
1032 env(
offer(bob, usd(500),
XRP(500)),
1045 musd.authorize({.account = alice});
1052 Balance(alice, startBalance - (f * 2)),
1056 Balance(bob, startBalance - (f * 2)),
1065 Balance(alice, startBalance - (f * 3) +
XRP(500)),
1069 Balance(bob, startBalance - (f * 2) -
XRP(500)),
1078 Env env{*
this, features};
1080 auto const f = env.
current()->fees().base;
1082 env.
fund(startBalance, gw, alice, bob);
1084 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
1090 env(
offer(alice,
XRP(1'000), usd(1000)),
1096 Balance(alice, startBalance - f - f),
1106 Balance(alice, startBalance - f - f - f +
XRP(50)),
1120 Balance(alice, startBalance - f - f - f - f +
XRP(100)),
1124 Balance(bob, startBalance - f - f -
XRP(100)),
1132 Env env(*
this, features);
1134 env.
fund(startBalance, gw, alice, bob);
1137 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {bob}});
1139 env(
pay(gw, bob, usd(1'000)));
1142 env(
offer(alice, usd(1'000),
XRP(2'000)));
1146 BEAST_EXPECT(aliceOffers.size() == 1);
1147 for (
auto const& offerPtr : aliceOffers)
1149 auto const&
offer = *offerPtr;
1150 BEAST_EXPECT(
offer[sfTakerGets] ==
XRP(2'000));
1151 BEAST_EXPECT(
offer[sfTakerPays] == usd(1'000));
1156 env(
offer(bob,
XRP(2'000), usd(1'000), tfPassive));
1161 BEAST_EXPECT(bobOffers.size() == 1);
1162 for (
auto const& offerPtr : bobOffers)
1164 auto const&
offer = *offerPtr;
1165 BEAST_EXPECT(
offer[sfTakerGets] == usd(1'000));
1166 BEAST_EXPECT(
offer[sfTakerPays] ==
XRP(2'000));
1170 env(
offer(gw,
XRP(2'000), usd(1'000)));
1176 env(
offer(gw, usd(1'000),
XRP(2'000)));
1184 Env env(*
this, features);
1186 env.
fund(startBalance, gw,
"alice",
"bob");
1189 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {bob}});
1191 env(
pay(gw,
"bob", usd(10'000)));
1192 env(
offer(
"alice", usd(5'000),
XRP(1'001)));
1195 env(
offer(
"alice", usd(5'000),
XRP(1'000)));
1199 BEAST_EXPECT(aliceOffers.size() == 2);
1204 env(
offer(
"bob",
XRP(2'000), usd(10'000), tfPassive));
1209 BEAST_EXPECT(bobOffers.size() == 1);
1210 for (
auto const& offerPtr : bobOffers)
1212 auto const&
offer = *offerPtr;
1213 BEAST_EXPECT(
offer[sfTakerGets] == usd(4'995));
1214 BEAST_EXPECT(
offer[sfTakerPays] ==
XRP(999));
1222 testcase(
"MPT AMM limitQuality checks rounded integral output");
1224 using namespace jtx;
1235 Env env{*
this, features};
1237 env.
fund(
XRP(10'000), gw, alice, bob);
1243 .holders = {alice, bob},
1246 AMM const amm(env, alice,
XRP(100), btc(1'000));
1248 auto const bobBTCBefore = btc.getBalance(bob);
1249 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1254 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1255 BEAST_EXPECT(btc.getBalance(bob) == bobBTCBefore + 32);
1256 BEAST_EXPECT(xrpAfter > xrpBefore);
1257 BEAST_EXPECT(btcAfter < btcBefore);
1258 BEAST_EXPECT(lpAfter == lpBefore);
1267 Env env{*
this, features};
1269 env.
fund(
XRP(10'000), gw, alice, bob);
1275 .holders = {alice, bob},
1278 AMM const amm(env, alice,
XRP(100), btc(1'000));
1280 auto const bobBTCBefore = btc.getBalance(bob);
1281 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1283 env(
offer(bob, btc(100),
drops(10'340'000)));
1286 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1287 BEAST_EXPECT(btc.getBalance(bob) == bobBTCBefore + 32);
1288 BEAST_EXPECT(xrpAfter > xrpBefore);
1289 BEAST_EXPECT(btcAfter < btcBefore);
1290 BEAST_EXPECT(lpAfter == lpBefore);
1294 if (BEAST_EXPECT(bobOffers.size() == 1))
1296 BEAST_EXPECT((*bobOffers[0])[sfTakerPays] != btc(100));
1297 BEAST_EXPECT((*bobOffers[0])[sfTakerGets] !=
drops(10'340'000));
1306 Env env{*
this, features};
1308 env.
fund(
XRP(10'000), gw, alice, bob);
1314 .holders = {alice, bob},
1317 AMM const amm(env, alice,
drops(1'000), btc(100'000'000));
1319 auto const bobXRPBefore = env.
balance(bob,
XRP);
1320 auto const baseFee = env.
current()->fees().base;
1321 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1326 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1328 BEAST_EXPECT(xrpAfter < xrpBefore);
1329 BEAST_EXPECT(btcAfter > btcBefore);
1330 BEAST_EXPECT(lpAfter == lpBefore);
1338 Env env{*
this, features};
1340 env.
fund(
XRP(10'000), gw, alice, bob);
1346 .holders = {alice, bob},
1349 AMM const amm(env, alice,
drops(1'000), btc(100'000'000));
1351 auto const bobXRPBefore = env.
balance(bob,
XRP);
1352 auto const baseFee = env.
current()->fees().base;
1353 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1355 env(
offer(bob,
drops(100), btc(10'340'000)));
1358 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1360 BEAST_EXPECT(xrpAfter < xrpBefore);
1361 BEAST_EXPECT(btcAfter > btcBefore);
1362 BEAST_EXPECT(lpAfter == lpBefore);
1366 if (BEAST_EXPECT(bobOffers.size() == 1))
1368 BEAST_EXPECT((*bobOffers[0])[sfTakerPays] !=
drops(100));
1369 BEAST_EXPECT((*bobOffers[0])[sfTakerGets] != btc(10'340'000));
1379 using namespace jtx;
1381 auto const startBalance =
XRP(1'000'000);
1382 auto const gw =
Account{
"gateway"};
1383 auto const alice =
Account{
"alice"};
1385 Env env{*
this, features};
1387 env.
fund(startBalance, gw, alice);
1389 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
1419 using namespace jtx;
1421 auto const gw =
Account{
"gateway"};
1422 auto const alice =
Account{
"alice"};
1423 auto const bob =
Account{
"bob"};
1425 auto const startBalance =
XRP(1'000'000);
1426 auto const xrpOffer =
XRP(1'000);
1428 Env env{*
this, features};
1430 env.
fund(startBalance, gw, alice, bob);
1433 auto const f = env.
current()->fees().base;
1435 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
1436 auto const usdOffer = usd(1'000);
1441 Balance(alice, startBalance - f),
1447 env(
offer(alice, xrpOffer, usdOffer),
1452 Balance(alice, startBalance - f - f),
1459 env(
offer(alice, xrpOffer, usdOffer),
1463 Balance(alice, startBalance - f - f - f),
1471 Balance(alice, startBalance - f - f - f),
1480 Balance(alice, startBalance - f - f - f),
1484 Balance(bob, startBalance - f),
1495 using namespace jtx;
1497 auto const gw =
Account{
"gateway"};
1499 auto const xrpOffer =
XRP(1'000);
1501 Env env{*
this, features};
1506 auto const f = env.
current()->fees().base;
1512 auto const usdOffer = usd(1'000);
1547 using namespace jtx;
1548 auto const gw =
Account{
"gateway"};
1549 auto const partner =
Account{
"partner"};
1551 auto test = [&](
auto&& issue1,
auto&& issue2) {
1552 Env env{*
this, features};
1556 auto const usd = issue1({.env = env, .token =
"USD", .issuer = gw});
1557 auto const btc = issue2({.env = env, .token =
"BTC", .issuer = gw});
1562 env.
fund(
XRP(10'000), partner);
1565 env(
trust(partner, usd(100)));
1574 env(
trust(partner, btc(500)));
1581 env(
pay(gw, partner, usd(100)));
1582 env(
pay(gw, partner, btc(500)));
1584 auto const& accountToTest = usePartner ? partner : gw;
1592 env(
offer(accountToTest, btc(250),
XRP(1'000)));
1596 BEAST_EXPECT(
isOffer(env, accountToTest, btc(250),
XRP(1'000)));
1598 auto const secondLegSeq = env.
seq(accountToTest);
1599 env(
offer(accountToTest,
XRP(1'000), usd(50)));
1603 BEAST_EXPECT(
isOffer(env, accountToTest,
XRP(1'000), usd(50)));
1607 env(
offer(accountToTest, usd(50), btc(250)));
1610 BEAST_EXPECT(jrr[jss::offers].isArray());
1611 BEAST_EXPECT(jrr[jss::offers].size() == 0);
1614 BEAST_EXPECT(jrr[jss::offers].isArray());
1615 BEAST_EXPECT(jrr[jss::offers].size() == 0);
1622 BEAST_EXPECT(acctOffers.empty());
1623 for (
auto const& offerPtr : acctOffers)
1625 auto const&
offer = *offerPtr;
1626 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
1627 BEAST_EXPECT(
offer[sfTakerGets] == usd(0));
1628 BEAST_EXPECT(
offer[sfTakerPays] ==
XRP(0));
1641 env(
offer(accountToTest, btc(250), usd(50)));
1646 BEAST_EXPECT(
isOffer(env, accountToTest, btc(250), usd(50)));
1649 BEAST_EXPECT(jrr[jss::offers].isArray());
1650 BEAST_EXPECT(jrr[jss::offers].size() == 0);
1654 env(
offer(accountToTest, usd(50), btc(250)));
1660 BEAST_EXPECT(jrr[jss::offers].isArray());
1661 BEAST_EXPECT(jrr[jss::offers].size() == 0);
1663 BEAST_EXPECT(
isOffer(env, accountToTest, usd(50), btc(250)));
1675 using namespace jtx;
1676 FeatureBitset const localFeatures = features | fixReducedOffersV2;
1678 Env env{*
this, localFeatures};
1680 auto const gw =
Account{
"gateway"};
1681 auto const alice =
Account{
"alice"};
1682 auto const bob =
Account{
"bob"};
1686 auto const gwInitialBalance =
drops(1'149'999'730);
1687 auto const aliceInitialBalance =
drops(499'946'999'680);
1688 auto const bobInitialBalance =
drops(10'199'999'920);
1690 env.
fund(gwInitialBalance, gw);
1691 env.
fund(aliceInitialBalance, alice);
1692 env.
fund(bobInitialBalance, bob);
1695 {.env = env, .issuer = gw, .holders = {alice, bob}, .transferFee = 5'000});
1696 MPT const usd = musd;
1697 auto const smallAmount =
STAmount{usd, 1};
1699 env(
pay(gw, alice, usd(50)));
1700 env(
pay(gw, bob, smallAmount));
1702 env(
offer(alice, usd(50),
XRP(150'000)));
1705 env(
pay(alice, gw, usd(50)));
1710 BEAST_EXPECT(!jrr[jss::node].isMember(sfMPTAmount.fieldName));
1713 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"1");
1717 env(
offer(bob,
XRP(2000), usd(1)));
1723 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"1");
1726 BEAST_EXPECT(bobOffer[sfTakerGets.jsonName][jss::value] ==
"1");
1727 BEAST_EXPECT(bobOffer[sfTakerPays.jsonName] ==
"2000000000");
1734 std::string(
"Offer Crossing with XRP, ") + (reverseOrder ?
"Reverse" :
"Normal") +
1737 using namespace jtx;
1739 Env env{*
this, features};
1741 auto const gw =
Account{
"gateway"};
1742 auto const alice =
Account{
"alice"};
1743 auto const bob =
Account{
"bob"};
1745 env.
fund(
XRP(10'000), gw, alice, bob);
1747 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
1749 env(
pay(gw, alice, usd(500)));
1752 env(
offer(bob, usd(1),
XRP(4'000)));
1754 env(
offer(alice,
XRP(150'000), usd(50)));
1757 env(
offer(bob, usd(1),
XRP(4000)));
1764 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"1");
1767 jrr[jss::node][sfBalance.fieldName] ==
1769 (
XRP(10000) -
XRP(reverseOrder ? 4000 : 3000) - env.
current()->fees().base * 2)
1773 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"499");
1776 jrr[jss::node][sfBalance.fieldName] ==
1778 (
XRP(10000) +
XRP(reverseOrder ? 4000 : 3000) - env.
current()->fees().base * 2)
1785 testcase(
"Offer Crossing with Limit Override");
1787 using namespace jtx;
1789 Env env{*
this, features};
1791 auto const gw =
Account{
"gateway"};
1792 auto const alice =
Account{
"alice"};
1793 auto const bob =
Account{
"bob"};
1795 env.
fund(
XRP(100000), gw, alice, bob);
1797 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
1799 env(
pay(gw, alice, usd(500)));
1801 env(
offer(alice,
XRP(150'000), usd(50)));
1802 env(
offer(bob, usd(1),
XRP(3'000)));
1805 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"1");
1808 jrr[jss::node][sfBalance.fieldName] ==
1812 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"499");
1815 jrr[jss::node][sfBalance.fieldName] ==
1822 testcase(
"Offer Accept then Cancel.");
1824 using namespace jtx;
1826 Env env{*
this, features};
1830 auto const nextOfferSeq = env.
seq(env.
master);
1835 BEAST_EXPECT(env.
seq(env.
master) == nextOfferSeq + 2);
1840 BEAST_EXPECT(env.
seq(env.
master) == nextOfferSeq + 2);
1846 testcase(
"Currency Conversion: Entire Offer");
1848 using namespace jtx;
1850 Env env{*
this, features};
1852 auto const gw =
Account{
"gateway"};
1853 auto const alice =
Account{
"alice"};
1854 auto const bob =
Account{
"bob"};
1856 env.
fund(
XRP(10'000), gw, alice, bob);
1859 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
1863 env(
pay(gw, alice, usd(100)));
1864 auto const bobOfferSeq = env.
seq(bob);
1865 env(
offer(bob, usd(100),
XRP(500)));
1869 BEAST_EXPECT(jro[jss::node][jss::TakerGets] ==
XRP(500).value().getText());
1876 BEAST_EXPECT(!jrr[jss::node].isMember(sfMPTAmount.fieldName));
1879 jrr[jss::node][sfBalance.fieldName] ==
1883 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"100");
1886 BEAST_EXPECT(jro[jss::error] ==
"entryNotFound");
1894 testcase(
"Currency Conversion: Offerer Into Debt");
1896 using namespace jtx;
1897 auto const alice =
Account{
"alice"};
1898 auto const bob =
Account{
"bob"};
1899 auto const carol =
Account{
"carol"};
1901 auto test = [&](
auto&& issue1,
auto&& issue2,
auto&& issue3) {
1902 Env env{*
this, features};
1904 env.
fund(
XRP(10'000), alice, bob, carol);
1907 issue1({.env = env, .token =
"USD", .issuer = alice, .holders = {bob}});
1909 issue2({.env = env, .token =
"EUC", .issuer = carol, .holders = {alice}});
1911 issue3({.env = env, .token =
"EUB", .issuer = bob, .holders = {carol}});
1913 auto const bobOfferSeq = env.
seq(bob);
1916 env(
offer(alice, eurc(200), usd(50)));
1919 BEAST_EXPECT(jro[jss::error] ==
"entryNotFound");
1927 testcase(
"Currency Conversion: In Parts");
1929 using namespace jtx;
1931 Env env{*
this, features};
1933 auto const gw =
Account{
"gateway"};
1934 auto const alice =
Account{
"alice"};
1935 auto const bob =
Account{
"bob"};
1937 env.
fund(
XRP(10'000), gw, alice, bob);
1939 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
1941 env(
pay(gw, alice, usd(200)));
1943 auto const bobOfferSeq = env.
seq(bob);
1944 env(
offer(bob, usd(100),
XRP(500)));
1950 BEAST_EXPECT(jro[jss::node][jss::TakerGets] ==
XRP(300).value().getText());
1957 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"160");
1961 jrr[jss::node][sfBalance.fieldName] ==
1966 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"40");
1978 BEAST_EXPECT(jro[jss::error] ==
"entryNotFound");
1986 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"100");
1989 jrr[jss::node][sfBalance.fieldName] ==
1995 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"100");
2001 testcase(
"Cross Currency Payment: Start with XRP");
2003 using namespace jtx;
2005 Env env{*
this, features};
2007 auto const gw =
Account{
"gateway"};
2008 auto const alice =
Account{
"alice"};
2009 auto const bob =
Account{
"bob"};
2010 auto const carol =
Account{
"carol"};
2012 env.
fund(
XRP(10'000), gw, alice, bob, carol);
2014 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {carol, bob}});
2016 env(
pay(gw, carol, usd(500)));
2018 auto const carolOfferSeq = env.
seq(carol);
2019 env(
offer(carol,
XRP(500), usd(50)));
2024 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"25");
2027 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"475");
2032 BEAST_EXPECT(jro[jss::node][jss::TakerPays] ==
XRP(250).value().getText());
2038 testcase(
"Cross Currency Payment: End with XRP");
2040 using namespace jtx;
2042 Env env{*
this, features};
2044 auto const gw =
Account{
"gateway"};
2045 auto const alice =
Account{
"alice"};
2046 auto const bob =
Account{
"bob"};
2047 auto const carol =
Account{
"carol"};
2049 env.
fund(
XRP(10'000), gw, alice, bob, carol);
2051 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, carol}});
2053 env(
pay(gw, alice, usd(500)));
2055 auto const carolOfferSeq = env.
seq(carol);
2056 env(
offer(carol, usd(50),
XRP(500)));
2061 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"475");
2064 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"25");
2068 jrr[jss::node][sfBalance.fieldName] ==
2072 BEAST_EXPECT(jro[jss::node][jss::TakerGets] ==
XRP(250).value().getText());
2080 testcase(
"Cross Currency Payment: Bridged");
2082 using namespace jtx;
2083 auto const gw1 =
Account{
"gateway_1"};
2084 auto const gw2 =
Account{
"gateway_2"};
2085 auto const alice =
Account{
"alice"};
2086 auto const bob =
Account{
"bob"};
2087 auto const carol =
Account{
"carol"};
2088 auto const dan =
Account{
"dan"};
2090 auto test = [&](
auto&& issue1,
auto&& issue2) {
2091 Env env{*
this, features};
2093 env.
fund(
XRP(10'000), gw1, gw2, alice, bob, carol, dan);
2096 issue1({.env = env, .token =
"USD", .issuer = gw1, .holders = {alice, carol}});
2098 issue1({.env = env, .token =
"EUR", .issuer = gw2, .holders = {bob, dan}});
2100 env(
pay(gw1, alice, usd(500)));
2101 env(
pay(gw2, dan, eur(400)));
2103 auto const carolOfferSeq = env.
seq(carol);
2104 env(
offer(carol, usd(50),
XRP(500)));
2106 auto const danOfferSeq = env.
seq(dan);
2107 env(
offer(dan,
XRP(500), eur(50)));
2110 jtp[0u][0u][jss::currency] =
"XRP";
2111 env(
pay(alice, bob, eur(30)),
Json(jss::Paths, jtp),
Sendmax(usd(333)));
2113 BEAST_EXPECT(env.
balance(alice, usd) == usd(470));
2114 BEAST_EXPECT(env.
balance(bob, eur) == eur(30));
2115 BEAST_EXPECT(env.
balance(carol, usd) == usd(30));
2116 BEAST_EXPECT(env.
balance(dan, eur) == eur(370));
2119 BEAST_EXPECT(jro[jss::node][jss::TakerGets] ==
XRP(200).value().getText());
2121 jro[jss::node][jss::TakerPays] ==
2126 jro[jss::node][jss::TakerGets] ==
2128 BEAST_EXPECT(jro[jss::node][jss::TakerPays] ==
XRP(200).value().getText());
2139 testcase(
"Auto Bridged Second Leg Dry");
2141 using namespace jtx;
2147 auto test = [&](
auto&& issue1,
auto&& issue2) {
2148 Env env(*
this, features);
2150 env.
fund(
XRP(100'000'000), alice, bob, carol, gw);
2154 issue1({.env = env, .token =
"USD", .issuer = gw, .holders = {alice, carol}});
2155 auto const eur = issue1({.env = env, .token =
"EUR", .issuer = gw, .holders = {bob}});
2157 env(
pay(gw, alice, usd(10)));
2158 env(
pay(gw, carol, usd(3)));
2176 env(
pay(gw, bob, eur(10)));
2178 env(
offer(bob, usd(10), eur(10)));
2203 testcase(
"Offer Fees Consume Funds");
2205 using namespace jtx;
2206 auto const gw1 =
Account{
"gateway_1"};
2207 auto const gw2 =
Account{
"gateway_2"};
2208 auto const gw3 =
Account{
"gateway_3"};
2209 auto const alice =
Account{
"alice"};
2210 auto const bob =
Account{
"bob"};
2212 auto test = [&](
auto&& issue1,
auto&& issue2,
auto&& issue3) {
2213 Env env{*
this, features};
2220 auto const base = env.
current()->fees().base;
2221 auto const startingXrp =
2222 XRP(100) + env.
current()->fees().accountReserve(3, 1) + base * 4;
2224 env.
fund(startingXrp, gw1, gw2, gw3, alice, bob);
2228 issue1({.env = env, .token =
"US1", .issuer = gw1, .holders = {alice, bob}});
2230 issue2({.env = env, .token =
"US2", .issuer = gw2, .holders = {alice, bob}});
2232 issue3({.env = env, .token =
"US3", .issuer = gw3, .holders = {alice}});
2234 env(
pay(gw1, bob, usD1(500)));
2236 env(
offer(bob,
XRP(200), usD1(200)));
2239 env(
offer(alice, usD1(200),
XRP(200)));
2241 BEAST_EXPECT(env.
balance(alice, usD1) == usD1(100));
2245 BEAST_EXPECT(env.
balance(bob, usD1) == usD1(400));
2253 testcase(
"Offer Create, then Cross");
2255 using namespace jtx;
2257 Env env{*
this, features};
2259 auto const gw =
Account{
"gateway"};
2260 auto const alice =
Account{
"alice"};
2261 auto const bob =
Account{
"bob"};
2263 env.
fund(
XRP(10'000), gw, alice, bob);
2266 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .transferFee = 5'000});
2268 env(
pay(gw, bob, cur(100)));
2270 env(
offer(alice, cur(50'000),
XRP(150'000)));
2271 env(
offer(bob,
XRP(100), cur(100)));
2274 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"34");
2277 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"64");
2283 testcase(
"Offer tfSell: Basic Sell");
2285 using namespace jtx;
2287 Env env{*
this, features};
2289 auto const gw =
Account{
"gateway"};
2290 auto const alice =
Account{
"alice"};
2291 auto const bob =
Account{
"bob"};
2293 auto const startingXrp =
2294 XRP(100) + env.
current()->fees().accountReserve(1, 1) + env.
current()->fees().base * 2;
2296 env.
fund(startingXrp, gw, alice, bob);
2298 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
2300 env(
pay(gw, bob, usd(500)));
2302 env(
offer(bob,
XRP(200), usd(200)),
Json(jss::Flags, tfSell));
2306 env(
offer(alice, usd(200),
XRP(200)),
Json(jss::Flags, tfSell));
2309 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"100");
2312 jrr[jss::node][sfBalance.fieldName] ==
2316 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"400");
2322 testcase(
"Offer tfSell: 2x Sell Exceed Limit");
2324 using namespace jtx;
2326 Env env{*
this, features};
2328 auto const gw =
Account{
"gateway"};
2329 auto const alice =
Account{
"alice"};
2330 auto const bob =
Account{
"bob"};
2332 auto const startingXrp =
2333 XRP(100) + env.
current()->fees().accountReserve(1, 1) + env.
current()->fees().base * 2;
2335 env.
fund(startingXrp, gw, alice, bob);
2337 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
2339 env(
pay(gw, bob, usd(500)));
2341 env(
offer(bob,
XRP(100), usd(200)));
2347 env(
offer(alice, usd(100),
XRP(100)),
Json(jss::Flags, tfSell));
2350 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"200");
2353 jrr[jss::node][sfBalance.fieldName] ==
2357 BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] ==
"300");
2363 testcase(
"Client Issue #535: Gateway Cross Currency");
2365 using namespace jtx;
2366 auto const gw =
Account{
"gateway"};
2367 auto const alice =
Account{
"alice"};
2368 auto const bob =
Account{
"bob"};
2370 auto test = [&](
auto&& issue1,
auto&& issue2) {
2371 Env env{*
this, features};
2373 auto const base = env.
current()->fees().base;
2374 auto const startingXrp =
2375 XRP(100.1) + env.
current()->fees().accountReserve(1, 1) + base * 2;
2377 env.
fund(startingXrp, gw, alice, bob);
2381 issue1({.env = env, .token =
"XTS", .issuer = gw, .holders = {alice, bob}});
2383 issue2({.env = env, .token =
"XXX", .issuer = gw, .holders = {alice, bob}});
2386 env(
pay(gw, alice, xts(1'000)));
2387 env(
pay(gw, alice, xxx(100)));
2388 env(
pay(gw, bob, xts(1'000)));
2389 env(
pay(gw, bob, xxx(100)));
2391 env(
offer(alice, xts(1'000), xxx(100)));
2398 payment[jss::id] = env.
seq(bob);
2399 payment[jss::build_path] =
true;
2400 payment[jss::tx_json] =
pay(bob, bob, xxx(1));
2401 payment[jss::tx_json][jss::Sequence] =
2404 payment[jss::tx_json][jss::SendMax] =
2406 auto jrr = wsc->invoke(
"submit", payment);
2407 BEAST_EXPECT(jrr[jss::status] ==
"success");
2408 BEAST_EXPECT(jrr[jss::result][jss::engine_result] ==
"tesSUCCESS");
2409 if (wsc->version() == 2)
2411 BEAST_EXPECT(jrr.isMember(jss::jsonrpc) && jrr[jss::jsonrpc] ==
"2.0");
2412 BEAST_EXPECT(jrr.isMember(jss::ripplerpc) && jrr[jss::ripplerpc] ==
"2.0");
2413 BEAST_EXPECT(jrr.isMember(jss::id) && jrr[jss::id] == 5);
2416 BEAST_EXPECT(env.
balance(alice, xts) == xts(1010));
2417 BEAST_EXPECT(env.
balance(alice, xxx) == xxx(99));
2419 BEAST_EXPECT(env.
balance(bob, xts) == xts(990));
2420 BEAST_EXPECT(env.
balance(bob, xxx) == xxx(101));
2433 using namespace jtx;
2435 auto const gw =
Account(
"gateway");
2437 Env env{*
this, features};
2439 env.
fund(
XRP(10'000'000), gw);
2442 auto musd =
MPTTester({.env = env, .issuer = gw});
2443 MPT const usd = musd;
2446 auto const f = env.
current()->fees().base;
2449 enum class PreAuthType { NoPreAuth, AcctPreAuth };
2455 PreAuthType preAuth;
2466 TestData
const tests[]{
2468 {.account=
"ann", .fundXrp=
reserve(env, 0) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=f, .balanceUsd=usd( 0), .offers=0, .owners=0},
2469 {.account=
"bev", .fundXrp=
reserve(env, 0) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=f, .balanceUsd=usd( 0), .offers=0, .owners=0},
2470 {.account=
"cam", .fundXrp=
reserve(env, 0) + 2 * f, .bookAmount=0, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tecINSUF_RESERVE_OFFER, .spentXrp=f, .balanceUsd=usd( 0), .offers=0, .owners=0},
2471 {.account=
"deb", .fundXrp=
reserve(env, 0) + 2 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=2 * f, .balanceUsd=usd( 1), .offers=0, .owners=1, .scale=100000},
2472 {.account=
"eve", .fundXrp=
reserve(env, 1) + 0 * f, .bookAmount=0, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=f, .balanceUsd=usd( 0), .offers=1, .owners=1},
2473 {.account=
"flo", .fundXrp=
reserve(env, 1) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + f, .balanceUsd=usd( 1), .offers=0, .owners=1},
2474 {.account=
"gay", .fundXrp=
reserve(env, 1) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 50) + f, .balanceUsd=usd( 50), .offers=0, .owners=1},
2475 {.account=
"hye", .fundXrp=
XRP(1000) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 800) + f, .balanceUsd=usd( 800), .offers=0, .owners=1},
2476 {.account=
"ivy", .fundXrp=
XRP( 1) +
reserve(env, 1) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + f, .balanceUsd=usd( 1), .offers=0, .owners=1},
2477 {.account=
"joy", .fundXrp=
XRP( 1) +
reserve(env, 2) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + f, .balanceUsd=usd( 1), .offers=1, .owners=2},
2478 {.account=
"kim", .fundXrp=
XRP( 900) +
reserve(env, 2) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=0, .owners=1},
2479 {.account=
"liz", .fundXrp=
XRP( 998) +
reserve(env, 0) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 998) + f, .balanceUsd=usd( 998), .offers=0, .owners=1},
2480 {.account=
"meg", .fundXrp=
XRP( 998) +
reserve(env, 1) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=0, .owners=1},
2481 {.account=
"nia", .fundXrp=
XRP( 998) +
reserve(env, 2) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=1, .owners=2},
2482 {.account=
"ova", .fundXrp=
XRP( 999) +
reserve(env, 0) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=0, .owners=1},
2483 {.account=
"pam", .fundXrp=
XRP( 999) +
reserve(env, 1) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP(1000) + f, .balanceUsd=usd( 1000), .offers=0, .owners=1},
2484 {.account=
"rae", .fundXrp=
XRP( 999) +
reserve(env, 2) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP(1000) + f, .balanceUsd=usd( 1000), .offers=0, .owners=1},
2485 {.account=
"sue", .fundXrp=
XRP(1000) +
reserve(env, 2) + 1 * f, .bookAmount=0, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=f, .balanceUsd=usd( 0), .offers=1, .owners=1},
2488 {.account=
"ned", .fundXrp=
reserve(env, 1) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2489 {.account=
"ole", .fundXrp=
reserve(env, 1) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2490 {.account=
"pat", .fundXrp=
reserve(env, 1) + 2 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2491 {.account=
"quy", .fundXrp=
reserve(env, 1) + 2 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2492 {.account=
"ron", .fundXrp=
reserve(env, 1) + 3 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecINSUF_RESERVE_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2493 {.account=
"syd", .fundXrp=
reserve(env, 1) + 3 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=3 * f, .balanceUsd=usd( 1), .offers=0, .owners=1, .scale=100000},
2494 {.account=
"ted", .fundXrp=
XRP( 20) +
reserve(env, 1) + 2 * f, .bookAmount=1000, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP(20) + 2 * f, .balanceUsd=usd( 20), .offers=0, .owners=1},
2495 {.account=
"uli", .fundXrp=
reserve(env, 2) + 0 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecINSUF_RESERVE_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2496 {.account=
"vic", .fundXrp=
reserve(env, 2) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + 2 * f, .balanceUsd=usd( 1), .offers=0, .owners=1},
2497 {.account=
"wes", .fundXrp=
reserve(env, 2) + 1 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=1, .owners=2},
2498 {.account=
"xan", .fundXrp=
reserve(env, 2) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + 2 * f, .balanceUsd=usd( 1), .offers=1, .owners=2},
2502 for (
auto const& t :
tests)
2504 auto const acct =
Account(t.account);
2505 env.
fund(t.fundXrp, acct);
2512 auto const book = t.bookAmount;
2514 env(
offer(gw,
XRP(book), usd(book * t.scale)));
2521 if (t.preAuth == PreAuthType::AcctPreAuth)
2522 musd.authorize({.account = acct});
2527 auto const acctOffer = t.offerAmount;
2528 env(
offer(acct, usd(acctOffer * t.scale),
XRP(acctOffer)),
Ter(t.tec));
2533 auto const expBalanceUsd = [&]() {
2535 return t.balanceUsd;
2538 return usd(f.value() / 10);
2540 BEAST_EXPECT(env.
balance(acct, usd) == expBalanceUsd);
2541 BEAST_EXPECT(env.
balance(acct,
xrpIssue()) == t.fundXrp - t.spentXrp);
2546 BEAST_EXPECT(acctOffers.size() == t.offers);
2547 if (!acctOffers.empty() && t.offers != 0)
2549 auto const& acctOffer = *(acctOffers.front());
2551 auto const leftover = t.offerAmount - t.bookAmount;
2552 BEAST_EXPECT(acctOffer[sfTakerGets] ==
XRP(leftover));
2553 BEAST_EXPECT(acctOffer[sfTakerPays] == usd(leftover));
2556 if (t.preAuth == PreAuthType::NoPreAuth)
2558 if (t.balanceUsd.value().signum() != 0)
2561 BEAST_EXPECT(env.
balance(acct, usd) == expBalanceUsd);
2584 using namespace jtx;
2586 auto const gw =
Account(
"gateway");
2587 auto const alice =
Account(
"alice");
2588 auto const bob =
Account(
"bob");
2590 Env env{*
this, features};
2592 env.
fund(
XRP(1'000'000), gw, bob);
2596 auto const fee = env.
current()->fees().base;
2603 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
2605 auto const usdOffer = usd(1'000);
2606 auto const xrpOffer =
XRP(1'000);
2608 env(
pay(gw, alice, usdOffer));
2615 auto const aliceXRP = env.
balance(alice);
2616 auto const bobsXRP = env.
balance(bob);
2618 env(
offer(alice, xrpOffer, usdOffer));
2620 env(
offer(bob, usdOffer, xrpOffer));
2626 Balance(alice, aliceXRP + xrpOffer - fee),
2627 Balance(bob, bobsXRP - xrpOffer - fee),
2631 BEAST_EXPECT(env.
balance(bob, usd) == usdOffer);
2634 env(
offer(alice, usd(999),
XRP(999)));
2635 env(
offer(bob, xrpOffer, usdOffer));
2641 BEAST_EXPECT(env.
balance(bob, usd) == usd(1));
2644 BEAST_EXPECT(bobsOffers.size() == 1);
2645 auto const& bobsOffer = *(bobsOffers.front());
2647 BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
2648 BEAST_EXPECT(bobsOffer[sfTakerGets] == usd(1));
2649 BEAST_EXPECT(bobsOffer[sfTakerPays] ==
XRP(1));
2658 using namespace jtx;
2659 auto const gw =
Account(
"gateway");
2660 auto const alice =
Account(
"alice");
2661 auto const bob =
Account(
"bob");
2663 auto test = [&](
auto&& issue1,
auto&& issue2) {
2664 Env env{*
this, features};
2670 auto const fee = env.
current()->fees().base;
2678 auto const usd = issue1({.env = env, .token =
"USD", .issuer = gw, .holders = {alice}});
2679 auto const eur = issue2({.env = env, .token =
"EUR", .issuer = gw, .holders = {bob}});
2681 auto const usdOffer = usd(1'000);
2682 auto const eurOffer = eur(1'000);
2684 env(
pay(gw, alice, usdOffer));
2685 env(
pay(gw, bob, eurOffer));
2693 env(
offer(alice, eurOffer, usdOffer));
2694 env(
offer(bob, usdOffer, eurOffer));
2702 BEAST_EXPECT(env.
balance(alice, eur) == eurOffer);
2703 BEAST_EXPECT(env.
balance(bob, usd) == usdOffer);
2708 env(
offer(bob, eurOffer, usdOffer));
2711 env(
offer(alice, usd(999), eurOffer));
2724 if (BEAST_EXPECT(bobsOffers.size() == 1))
2726 auto const& bobsOffer = *(bobsOffers.front());
2728 BEAST_EXPECT(bobsOffer[sfTakerGets] == usd(1));
2729 BEAST_EXPECT(bobsOffer[sfTakerPays] == eur(1));
2734 env(
offer(alice, usd(1), eur(1)));
2749 auto meur =
MPTTester(env, gw, eur, {bob});
2751 meur.
authorize({.account = alice, .flags = tfMPTUnauthorize});
2756 auto musd =
MPTTester(env, gw, usd, {alice});
2758 musd.
authorize({.account = bob, .flags = tfMPTUnauthorize});
2763 env(
offer(alice, eur(999), usdOffer));
2766 env(
offer(bob, usdOffer, eurOffer));
2785 using namespace jtx;
2786 auto const gw =
Account(
"gateway");
2787 auto const alice =
Account(
"alice");
2788 auto const bob =
Account(
"bob");
2789 auto const carol =
Account(
"carol");
2791 auto test = [&](
auto&& issue1,
auto&& issue2) {
2792 Env env{*
this, features};
2794 env.
fund(
XRP(1'000'000), gw, alice, bob, carol);
2797 auto const usd = issue1({.env = env, .token =
"USD", .issuer = gw, .holders = {alice}});
2798 auto const eur = issue2({.env = env, .token =
"EUR", .issuer = gw, .holders = {carol}});
2800 auto const usdOffer = usd(1'000);
2801 auto const eurOffer = eur(1'000);
2803 env(
pay(gw, alice, usdOffer));
2804 env(
pay(gw, carol, eurOffer));
2813 env(
offer(alice,
XRP(1'000), usdOffer));
2814 env(
offer(bob, eurOffer,
XRP(1'000)));
2815 auto const bobXrpBalance = env.
balance(bob);
2820 env(
offer(carol, usd(400), eur(400)));
2829 BEAST_EXPECT(env.
balance(bob, eur) == eur(400));
2830 BEAST_EXPECT(env.
balance(carol, usd) == usd(400));
2833 BEAST_EXPECT(aliceOffers.size() == 1);
2834 auto const& aliceOffer = *(aliceOffers.front());
2836 BEAST_EXPECT(aliceOffer[sfLedgerEntryType] == ltOFFER);
2837 BEAST_EXPECT(aliceOffer[sfTakerGets] == usd(600));
2838 BEAST_EXPECT(aliceOffer[sfTakerPays] ==
XRP(600));
2842 BEAST_EXPECT(bobsOffers.size() == 1);
2843 auto const& bobsOffer = *(bobsOffers.front());
2845 BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
2846 BEAST_EXPECT(bobsOffer[sfTakerGets] ==
XRP(600));
2847 BEAST_EXPECT(bobsOffer[sfTakerPays] == eur(600));
2852 env(
offer(carol, usd(600), eur(600)));
2862 BEAST_EXPECT(env.
balance(bob, eur) == eur(1'000));
2863 BEAST_EXPECT(env.
balance(carol, usd) == usd(1'000));
2867 if (!aliceOffers.empty())
2869 BEAST_EXPECT(aliceOffers.size() == 1);
2870 auto const& aliceOffer = *(aliceOffers.front());
2872 BEAST_EXPECT(aliceOffer[sfLedgerEntryType] == ltOFFER);
2873 BEAST_EXPECT(aliceOffer[sfTakerGets] == usd(0));
2874 BEAST_EXPECT(aliceOffer[sfTakerPays] ==
XRP(0));
2888 using namespace jtx;
2890 auto const gw =
Account(
"gateway");
2892 Env env{*
this, features};
2894 env.
fund(
XRP(10'000'000), gw);
2896 auto musd =
MPTTester({.env = env, .issuer = gw});
2897 MPT const usd = musd;
2900 auto const f = env.
current()->fees().base;
2936 : account(std::move(account))
2937 , fundXrp(std::move(fundXrp))
2938 , fundUSD(std::move(fundUsd))
2939 , gwGets(std::move(gwGets))
2940 , gwPays(std::move(gwPays))
2941 , acctGets(std::move(acctGets))
2942 , acctPays(std::move(acctPays))
2944 , spentXrp(std::move(spentXrp))
2945 , finalUsd(std::move(finalUsd))
2948 , takerGets(std::move(takerGets))
2949 , takerPays(std::move(takerPays))
2987 TestData
const tests[]{
2990 {
"ann",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd( 5), usd(10),
XRP(10),
tecINSUF_RESERVE_OFFER,
XRP( 0) + (1 * f), usd( 0), 0, 0},
2991 {
"bev",
XRP(10) +
reserve(env, 1) + 1 * f, usd( 0),
XRP(10), usd( 5), usd(10),
XRP(10),
tesSUCCESS,
XRP( 0) + (1 * f), usd( 0), 1, 1,
XRP(10), usd(10)},
2992 {
"cam",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(10), usd(10),
XRP(10),
tesSUCCESS,
XRP( 10) + (1 * f), usd(10), 0, 1},
2993 {
"deb",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(20), usd(10),
XRP(10),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 0, 1},
2994 {
"eve",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(20), usd( 5),
XRP( 5),
tesSUCCESS,
XRP( 5) + (1 * f), usd(10), 0, 1},
2995 {
"flo",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(20), usd(20),
XRP(20),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 0, 1},
2996 {
"gay",
XRP(20) +
reserve(env, 1) + 1 * f, usd( 0),
XRP(10), usd(20), usd(20),
XRP(20),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 0, 1},
2997 {
"hye",
XRP(20) +
reserve(env, 2) + 1 * f, usd( 0),
XRP(10), usd(20), usd(20),
XRP(20),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 1, 2,
XRP(10), usd(10)},
2999 {
"meg",
reserve(env, 1) + 2 * f, usd(10), usd(10),
XRP( 5),
XRP(10), usd(10),
tecINSUF_RESERVE_OFFER,
XRP( 0) + (2 * f), usd(10), 0, 1},
3000 {
"nia",
reserve(env, 2) + 2 * f, usd(10), usd(10),
XRP( 5),
XRP(10), usd(10),
tesSUCCESS,
XRP( 0) + (2 * f), usd(10), 1, 2, usd(10),
XRP(10)},
3001 {
"ova",
reserve(env, 1) + 2 * f, usd(10), usd(10),
XRP(10),
XRP(10), usd(10),
tesSUCCESS,
XRP(-10) + (2 * f), usd( 0), 0, 1},
3002 {
"pam",
reserve(env, 1) + 2 * f, usd(10), usd(10),
XRP(20),
XRP(10), usd(10),
tesSUCCESS,
XRP(-20) + (2 * f), usd( 0), 0, 1},
3003 {
"qui",
reserve(env, 1) + 2 * f, usd(10), usd(20),
XRP(40),
XRP(10), usd(10),
tesSUCCESS,
XRP(-20) + (2 * f), usd( 0), 0, 1},
3004 {
"rae",
reserve(env, 2) + 2 * f, usd(10), usd( 5),
XRP( 5),
XRP(10), usd(10),
tesSUCCESS,
XRP( -5) + (2 * f), usd( 5), 1, 2, usd( 5),
XRP( 5)},
3005 {
"sue",
reserve(env, 2) + 2 * f, usd(10), usd( 5),
XRP(10),
XRP(10), usd(10),
tesSUCCESS,
XRP(-10) + (2 * f), usd( 5), 1, 2, usd( 5),
XRP( 5)},
3009 auto const zeroUsd = usd(0);
3010 for (
auto const& t :
tests)
3015 auto const acct =
Account(t.account);
3017 env.
fund(t.fundXrp, acct);
3023 if (t.fundUSD != zeroUsd)
3025 musd.authorize({.account = acct});
3027 env(
pay(gw, acct, t.fundUSD));
3031 env(
offer(gw, t.gwGets, t.gwPays));
3036 env(
offer(acct, t.acctGets, t.acctPays, tfSell),
Ter(t.tec));
3041 BEAST_EXPECT(env.
balance(acct, usd) == t.finalUsd);
3042 BEAST_EXPECT(env.
balance(acct,
xrpIssue()) == t.fundXrp - t.spentXrp);
3049 if (!acctOffers.empty())
3051 BEAST_EXPECT(acctOffers.size() == 1);
3052 auto const& acctOffer = *(acctOffers.front());
3054 BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER);
3055 BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets);
3056 BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays);
3073 testcase(
"Combine tfSell with tfFillOrKill");
3075 using namespace jtx;
3077 auto const gw =
Account(
"gateway");
3078 auto const alice =
Account(
"alice");
3079 auto const bob =
Account(
"bob");
3081 Env env{*
this, features};
3083 env.
fund(
XRP(10'000'000), gw, alice, bob);
3085 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {bob}});
3088 env(
pay(gw, bob, usd(100)));
3090 env(
offer(bob,
XRP(2'000), usd(20)));
3103 env(
offer(alice, usd(20),
XRP(2'000), tfSell | tfFillOrKill));
3112 env(
offer(bob,
XRP(2'000), usd(20)));
3114 env(
offer(alice, usd(10),
XRP(1'500), tfSell | tfFillOrKill));
3138 env(
offer(alice, usd(1),
XRP(500), tfSell | tfFillOrKill));
3151 using namespace jtx;
3152 auto const gw1 =
Account(
"gateway1");
3155 auto const issuer =
Account(
"issuer");
3156 auto const sender =
Account(
"sender");
3157 auto const receiver =
Account(
"receiver");
3158 auto const seller =
Account(
"seller");
3159 auto const buyer =
Account(
"buyer");
3161 Env env{*
this, features};
3162 env.
fund(
XRP(10'000), issuer, sender, receiver, seller, buyer);
3168 .holders = {sender, receiver, seller, buyer},
3169 .transferFee = 100}};
3172 mpt.
pay(issuer, sender, 2'000);
3173 mpt.
pay(issuer, seller, 2'000);
3182 BEAST_EXPECT(mpt.
getBalance(sender) == 1'000);
3183 BEAST_EXPECT(mpt.
getBalance(receiver) == 999);
3184 BEAST_EXPECT(mpt.
getBalance(issuer) == 3'999);
3193 BEAST_EXPECT(mpt.
getBalance(seller) == 1'000);
3195 BEAST_EXPECT(mpt.
getBalance(issuer) == 3'998);
3198 auto test = [&](
auto&& issue1,
auto&& issue2) {
3199 Env env{*
this, features};
3202 auto const fee = env.
current()->fees().base;
3208 issue1({.env = env, .token =
"USD", .issuer = gw1, .transferFee = 25'000});
3211 auto const ann =
Account(
"ann");
3212 auto const bob =
Account(
"bob");
3219 musd.authorize({.account = ann});
3220 musd.authorize({.account = bob});
3224 env(
trust(ann, usd(20'000)));
3225 env(
trust(bob, usd(20'000)));
3229 env(
pay(gw1, bob, usd(12'500)));
3238 env(
offer(bob,
XRP(1), usd(10'000)));
3241 env(
offer(ann, usd(10'000),
XRP(1)));
3256 auto const che =
Account(
"che");
3257 auto const deb =
Account(
"deb");
3264 musd.authorize({.account = che});
3265 musd.authorize({.account = deb});
3269 env(
trust(che, usd(20'000)));
3270 env(
trust(deb, usd(20'000)));
3274 env(
pay(gw1, deb, usd(12'500)));
3277 env(
offer(che, usd(10'000),
XRP(1)));
3280 env(
offer(deb,
XRP(1), usd(10'000)));
3292 auto const eve =
Account(
"eve");
3293 auto const fyn =
Account(
"fyn");
3295 env.
fund(
XRP(20'000) + (fee * 2), eve, fyn);
3301 musd.authorize({.account = eve});
3302 musd.authorize({.account = fyn});
3306 env(
trust(eve, usd(20'000)));
3307 env(
trust(fyn, usd(20'000)));
3311 env(
pay(gw1, eve, usd(10'000)));
3312 env(
pay(gw1, fyn, usd(10'000)));
3318 env(
offer(eve, usd(1'000),
XRP(4'000)));
3322 env(
offer(fyn,
XRP(2'000), usd(500)));
3328 BEAST_EXPECT(evesOffers.size() == 1);
3329 if (!evesOffers.empty())
3331 auto const& evesOffer = *(evesOffers.front());
3332 BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER);
3333 BEAST_EXPECT(evesOffer[sfTakerGets] ==
XRP(2'000));
3334 BEAST_EXPECT(evesOffer[sfTakerPays] == usd(500));
3343 auto const gw2 =
Account(
"gateway2");
3349 issue2({.env = env, .token =
"EUR", .issuer = gw2, .transferFee = 50'000});
3355 auto const gay =
Account(
"gay");
3356 auto const hal =
Account(
"hal");
3363 musd.authorize({.account = gay});
3364 musd.authorize({.account = hal});
3368 env(
trust(gay, usd(20'000)));
3369 env(
trust(hal, usd(20'000)));
3375 meur.authorize({.account = gay});
3376 meur.authorize({.account = hal});
3380 env(
trust(gay, eur(20'000)));
3381 env(
trust(hal, eur(20'000)));
3385 env(
pay(gw1, gay, usd(12'500)));
3386 env(
pay(gw2, hal, eur(150)));
3389 env(
offer(gay, eur(100), usd(10'000)));
3392 env(
offer(hal, usd(10'000), eur(100)));
3408 auto const ova =
Account(
"ova");
3409 auto const pat =
Account(
"pat");
3410 auto const qae =
Account(
"qae");
3420 musd.authorize({.account = ova});
3421 musd.authorize({.account = pat});
3422 musd.authorize({.account = qae});
3426 env(
trust(ova, usd(20'000)));
3427 env(
trust(pat, usd(20'000)));
3428 env(
trust(qae, usd(20'000)));
3434 meur.authorize({.account = ova});
3435 meur.authorize({.account = pat});
3436 meur.authorize({.account = qae});
3440 env(
trust(ova, eur(20'000)));
3441 env(
trust(pat, eur(20'000)));
3442 env(
trust(qae, eur(20'000)));
3446 env(
pay(gw1, ova, usd(12'500)));
3447 env(
pay(gw2, qae, eur(150)));
3450 env(
offer(ova,
XRP(2), usd(10'000)));
3454 env(
offer(qae, usd(10'000), eur(100)));
3463 if (!ovasOffers.empty())
3465 BEAST_EXPECT(ovasOffers.size() == 1);
3466 auto const& ovasOffer = *(ovasOffers.front());
3468 BEAST_EXPECT(ovasOffer[sfLedgerEntryType] == ltOFFER);
3469 BEAST_EXPECT(ovasOffer[sfTakerGets] == usd(0));
3470 BEAST_EXPECT(ovasOffer[sfTakerPays] ==
XRP(0));
3491 auto const gw =
Account(
"gw_tr1");
3492 auto const alice =
Account(
"alice_tr1");
3493 auto const bob =
Account(
"bob_tr1");
3495 Env env{*
this, features};
3496 env.
fund(
XRP(10'000), gw, alice, bob);
3500 {.env = env, .issuer = gw, .holders = {alice, bob}, .transferFee = 25'000});
3503 env(
pay(gw, alice, usd(1'250)));
3505 env(
offer(bob, usd(1'000),
XRP(1'000)));
3516 BEAST_EXPECT(env.
balance(alice, usd) == usd(0));
3518 BEAST_EXPECT(env.
balance(bob, usd) == usd(1'000));
3528 auto const gw =
Account(
"gw_tr2");
3529 auto const gw2 =
Account(
"gw2_tr2");
3530 auto const alice =
Account(
"alice_tr2");
3531 auto const bob =
Account(
"bob_tr2");
3532 auto const carol =
Account(
"carol_tr2");
3534 Env env{*
this, features};
3535 env.
fund(
XRP(10'000), gw, gw2, alice, bob, carol);
3539 {.env = env, .issuer = gw, .holders = {bob, carol}, .transferFee = 25'000});
3540 auto const gusd = gw2[
"USD"];
3542 env(
trust(alice, gusd(10'000)));
3543 env(
trust(bob, gusd(10'000)));
3546 env(
pay(gw2, alice, gusd(1'000)));
3547 env(
pay(gw, bob, musd(1'000)));
3551 env(
offer(bob, gusd(1'000), musd(1'000)));
3553 env(
offer(carol, musd(800),
XRP(800)));
3565 BEAST_EXPECT(env.
balance(alice, gusd) == gusd(0));
3567 BEAST_EXPECT(env.
balance(bob, musd) == musd(0));
3569 BEAST_EXPECT(env.
balance(carol, musd) == musd(800));
3576 testcase(
"Transfer Rate Overflow Offer");
3578 using namespace jtx;
3580 auto const issuer =
Account(
"issuer");
3581 auto const taker =
Account(
"taker");
3584 Env env{*
this, features};
3585 env.
fund(
XRP(10'000), issuer, taker);
3588 auto constexpr takerFunds = 2'000'000'000'000'000'000LL;
3593 .transferFee = 50'000,
3600 auto constexpr offerAmount = 1'230'000'000'000'000'000LL;
3601 auto const takerSeq = env.
seq(taker);
3615 Env env{*
this, features};
3616 env.
fund(
XRP(10'000), issuer, taker);
3620 {.env = env, .issuer = issuer, .holders = {taker}, .transferFee = 10'000}};
3625 std::int64_t const poisonAmount = 8'500'000'000'000'000'000LL;
3626 auto const poisonSeq = env.
seq(issuer);
3631 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3633 auto const takerSeq = env.
seq(taker);
3637 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3643 auto const gwA =
Account(
"gatewayA");
3644 auto const gwB =
Account(
"gatewayB");
3645 auto const alice =
Account(
"alice");
3646 auto const mallory =
Account(
"mallory");
3648 Env env{*
this, features};
3649 env.
fund(
XRP(10'000), gwA, gwB, alice, mallory);
3653 {.env = env, .issuer = gwA, .holders = {alice, mallory}, .transferFee = 50'000}};
3655 MPTTester const tokenB{{.env = env, .issuer = gwB, .holders = {alice, mallory}}};
3657 env(
pay(gwA, alice, tokenA(1'000)));
3663 std::int64_t const poisonPays = 6'148'914'691'236'517'205LL;
3664 std::int64_t const poisonGets = 34'000'000'000'000'000LL;
3665 env(
pay(gwB, mallory, tokenB(poisonGets)));
3667 auto const poisonSeq = env.
seq(mallory);
3668 env(
offer(mallory, tokenA(poisonPays), tokenB(poisonGets)));
3672 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3674 auto const aliceSeq = env.
seq(alice);
3675 env(
offer(alice, tokenB(1), tokenA(100)));
3678 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3704 Env env{*
this, features};
3705 env.
fund(
XRP(10'000), issuer, taker);
3709 {.env = env, .issuer = issuer, .holders = {taker}, .maxAmt =
kMaxMpTokenAmount}};
3714 auto const funded = 1'844'674'407'370'955'162LL;
3715 auto const offerOut = funded + 1;
3717 auto const poisonSeq = env.
seq(issuer);
3722 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3724 auto const issuerXRPBefore = env.
balance(issuer,
XRP);
3725 auto const takerXRPBefore = env.
balance(taker,
XRP);
3727 auto const takerSeqBefore = env.
seq(taker);
3729 auto const takerSeq = takerSeqBefore;
3730 auto const fee = env.
current()->fees().base;
3737 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3740 BEAST_EXPECT(env.
balance(issuer,
XRP) == issuerXRPBefore);
3741 BEAST_EXPECT(env.
balance(taker,
XRP) == takerXRPBefore - fee);
3742 BEAST_EXPECT(env.
balance(taker,
token) == takerMPTBefore);
3743 BEAST_EXPECT(env.
seq(taker) == takerSeqBefore + 1);
3747 auto const poisonMaker =
Account(
"poisonMaker");
3749 Env env{*
this, features};
3750 env.
fund(
XRP(10'000), issuer, poisonMaker, taker);
3756 .holders = {poisonMaker, taker},
3763 auto const funded = 1'844'674'407'370'955'162LL;
3764 auto const offerOut = funded + 1;
3765 env(
pay(issuer, poisonMaker,
token(funded)));
3767 auto const poisonSeq = env.
seq(poisonMaker);
3771 auto const poisonKeylet =
3773 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3775 auto const takerSeq = env.
seq(taker);
3779 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3800 env.
fund(
XRP(10'000), issuer, taker);
3804 {.env = env, .issuer = issuer, .holders = {taker}, .transferFee = 10'000}};
3806 std::int64_t const poisonAmount = 8'500'000'000'000'000'000LL;
3807 auto const poisonSeq = env.
seq(issuer);
3811 auto const poisonKeylet =
3813 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3815 auto const takerSeq = env.
seq(taker);
3819 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3823 BEAST_EXPECT(logs.contains(
"Removing offer with overflowing amount calculation"));
3844 using namespace jtx;
3846 auto const gw =
Account(
"gateway");
3848 Env env{*
this, features};
3851 auto const fee = env.
current()->fees().base;
3852 auto const startBalance =
XRP(1'000'000);
3854 env.
fund(startBalance + (fee * 5), gw);
3871 BEAST_EXPECT(gwOffers.size() == 3);
3872 for (
auto const& offerPtr : gwOffers)
3874 auto const&
offer = *offerPtr;
3875 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
3876 BEAST_EXPECT(
offer[sfTakerGets] ==
XRP(600));
3877 BEAST_EXPECT(
offer[sfTakerPays] == usd(60));
3882 env(
offer(gw,
XRP(1'000), usd(100)));
3889 BEAST_EXPECT(gwOffers.size() == 1);
3890 for (
auto const& offerPtr : gwOffers)
3892 auto const&
offer = *offerPtr;
3893 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
3894 BEAST_EXPECT(
offer[sfTakerGets] == usd(100));
3895 BEAST_EXPECT(
offer[sfTakerPays] ==
XRP(1'000));
3902 using namespace jtx;
3904 auto const gw1 =
Account(
"gateway1");
3905 auto const gw2 =
Account(
"gateway2");
3906 auto const alice =
Account(
"alice");
3908 auto test = [&](
auto&& issue1,
auto&& issue2) {
3909 Env env{*
this, features};
3911 env.
fund(
XRP(1'000'000), gw1, gw2);
3914 auto const usd = issue1({.env = env, .token =
"USD", .issuer = gw1});
3916 auto const eur = issue2({.env = env, .token =
"EUR", .issuer = gw2});
3920 auto const f = env.
current()->fees().base;
3934 TestData
const tests[]{
3945 for (
auto const& t :
tests)
3947 auto const acct =
Account{t.acct};
3948 env.
fund(t.fundXRP, acct);
3954 musd.authorize({.account = acct});
3958 env(
trust(acct, usd(1'000)));
3964 meur.authorize({.account = acct});
3968 env(
trust(acct, eur(1'000)));
3972 if (t.fundUSD > usd(0))
3973 env(
pay(gw1, acct, t.fundUSD));
3974 if (t.fundEUR > eur(0))
3975 env(
pay(gw2, acct, t.fundEUR));
3978 env(
offer(acct, usd(500), eur(600)),
Ter(t.firstOfferTec));
3982 int offerCount = t.firstOfferTec ==
tesSUCCESS ? 1 : 0;
3988 BEAST_EXPECT(acctOffers.size() == offerCount);
3989 for (
auto const& offerPtr : acctOffers)
3991 auto const&
offer = *offerPtr;
3992 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
3993 BEAST_EXPECT(
offer[sfTakerGets] == eur(600));
3994 BEAST_EXPECT(
offer[sfTakerPays] == usd(500));
3997 env(
offer(acct, eur(600), usd(500)),
Ter(t.secondOfferTec));
4001 offerCount = t.secondOfferTec ==
tesSUCCESS ? 1 : offerCount;
4007 BEAST_EXPECT(acctOffers.size() == offerCount);
4008 for (
auto const& offerPtr : acctOffers)
4010 auto const&
offer = *offerPtr;
4011 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
4012 if (
offer[sfSequence] == firstOfferSeq)
4014 BEAST_EXPECT(
offer[sfTakerGets] == eur(600));
4015 BEAST_EXPECT(
offer[sfTakerPays] == usd(500));
4019 BEAST_EXPECT(
offer[sfTakerGets] == usd(500));
4020 BEAST_EXPECT(
offer[sfTakerPays] == eur(600));
4049 using namespace jtx;
4051 Env env{*
this, features};
4053 auto const alice =
Account(
"alice");
4054 auto const bob =
Account(
"bob");
4055 auto const f = env.
current()->fees().base;
4057 env.
fund(
XRP(50'000) + f, alice, bob);
4060 MPT const usd =
MPTTester({.env = env, .issuer = bob});
4062 env(
offer(alice, usd(5'000),
XRP(50'000)));
4066 env(
offer(bob,
XRP(50'000), usd(5'000)));
4078 BEAST_EXPECT(bobOffers.size() == 1);
4079 for (
auto const& offerPtr : bobOffers)
4081 auto const&
offer = *offerPtr;
4082 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
4083 BEAST_EXPECT(
offer[sfTakerGets] == usd(25));
4084 BEAST_EXPECT(
offer[sfTakerPays] ==
XRP(250));
4097 using namespace jtx;
4098 auto const ann =
Account(
"ann");
4099 auto const bob =
Account(
"bob");
4100 auto const cam =
Account(
"cam");
4102 auto test = [&](
auto&& issue1,
auto&& issue2) {
4103 Env env{*
this, features};
4105 auto const fee = env.
current()->fees().base;
4106 env.
fund(
reserve(env, 4) + (fee * 5), ann, bob, cam);
4109 auto const aBux = issue1(
4110 {.env = env, .token =
"AUX", .issuer = ann, .holders = {cam}});
4111 auto const bBux = issue2(
4115 .holders = {ann, cam}});
4117 env(
pay(ann, cam, aBux(35)));
4118 env(
pay(bob, cam, bBux(35)));
4120 env(
offer(bob, aBux(30), bBux(30)));
4126 env(
offer(cam, aBux(29), bBux(30), tfPassive));
4133 env(
offer(cam, bBux(30), aBux(30)));
4150 testcase(
"Self crossing low quality offer");
4152 using namespace jtx;
4154 Env env{*
this, features};
4156 auto const ann =
Account(
"ann");
4157 auto const gw =
Account(
"gateway");
4159 auto const fee = env.
current()->fees().base;
4165 {.env = env, .issuer = gw, .holders = {ann}, .transferFee = 2'000});
4167 env(
pay(gw, ann, btc(2'856)));
4170 env(
offer(ann,
drops(365'611'702'030), btc(5'713)));
4174 env(
offer(ann, btc(687),
drops(20'000'000'000)),
4187 using namespace jtx;
4189 Env env{*
this, features};
4191 auto const gw =
Account(
"gateway");
4192 auto const alice =
Account(
"alice");
4193 auto const bob =
Account(
"bob");
4195 auto const fee = env.
current()->fees().base;
4200 MPT const cny =
MPTTester({.env = env, .issuer = gw, .holders = {bob}});
4202 env(
pay(gw, bob, cny(3'000'000)));
4205 env(
offer(bob,
drops(5'400'000'000), cny(2'160'540)));
4209 env(
offer(alice, cny(135'620'001),
drops(339'000'000'000)));
4213 BEAST_EXPECT(aliceOffers.size() == 1);
4214 for (
auto const& offerPtr : aliceOffers)
4216 auto const&
offer = *offerPtr;
4217 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
4218 BEAST_EXPECT(
offer[sfTakerGets] ==
drops(333'599'446'582));
4219 BEAST_EXPECT(
offer[sfTakerPays] == cny(13'3459'461));
4229 testcase(
"Offer In Scaling With Xfer Rate");
4231 using namespace jtx;
4232 auto const gw =
Account(
"gateway");
4233 auto const alice =
Account(
"alice");
4234 auto const bob =
Account(
"bob");
4236 auto test = [&](
auto&& issue1,
auto&& issue2) {
4237 Env env{*
this, features};
4239 auto const fee = env.
current()->fees().base;
4247 auto const jpy = issue1(
4253 .transferFee = 2'000});
4254 auto const btc = issue2(
4260 .transferFee = 2'000});
4262 env(
pay(gw, alice, jpy(3'699'034'802'280'317)));
4263 env(
pay(gw, bob, btc(115'672'255'914'031'100)));
4267 bob, jpy(1'241'913'390'770'747), btc(1'969'825'690'469'254)));
4272 alice, btc(5'507'568'706'427'876), jpy(3'472'696'773'391'072)));
4276 BEAST_EXPECT(aliceOffers.size() == 1);
4277 for (
auto const& offerPtr : aliceOffers)
4279 auto const&
offer = *offerPtr;
4280 BEAST_EXPECT(
offer[sfLedgerEntryType] == ltOFFER);
4289 BEAST_EXPECT(
offer[sfTakerGets] == jpy(2'230'659'191'281'247));
4290 BEAST_EXPECT(
offer[sfTakerPays] == btc(3'537'743'015'958'622));
4335 using namespace jtx;
4336 auto const gw =
Account(
"gw");
4338 auto test = [&](
auto&& issue1,
auto&& issue2) {
4339 Env env{*
this, features};
4340 auto const baseFee = env.
current()->fees().base.drops();
4342 auto const startXrpBalance =
XRP(4'000'000);
4344 env.
fund(startXrpBalance, gw);
4347 auto const btc = issue1(
4351 .transferFee = 25'000});
4354 auto const usd = issue2(
4358 .transferFee = 25'000});
4385 TestData
const tests[]{
4387 {0, 0, 1, btc(200), {{
"ann", 0,
drops(3900000'000000 - (4 * baseFee)), btc(200), usd(3000)}, {
"abe", 0,
drops(4100000'000000 - (3 * baseFee)), btc( 0), usd(750)}}},
4388 {0, 1, 0, btc(200), {{
"bev", 0,
drops(4100000'000000 - (4 * baseFee)), btc( 75), usd(2000)}, {
"bob", 0,
drops(3900000'000000 - (3 * baseFee)), btc(100), usd( 0)}}},
4389 {0, 0, 0, btc(200), {{
"cam", 0,
drops(4000000'000000 - (5 * baseFee)), btc(200), usd(2000)} }},
4390 {0, 1, 0, btc( 50), {{
"deb", 1,
drops(4040000'000000 - (4 * baseFee)), btc( 0), usd(2000)}, {
"dan", 1,
drops(3960000'000000 - (3 * baseFee)), btc( 40), usd( 0)}}},
4394 for (
auto const& t :
tests)
4396 Account const& self = t.actors[t.self].acct;
4397 Account const& leg0 = t.actors[t.leg0].acct;
4398 Account const& leg1 = t.actors[t.leg1].acct;
4400 for (
auto const& actor : t.actors)
4402 env.
fund(
XRP(4'000'000), actor.acct);
4408 mbtc.authorize({.account = actor.acct});
4412 env(
trust(actor.acct, btc(400)));
4418 musd.authorize({.account = actor.acct});
4422 env(
trust(actor.acct, usd(8000)));
4428 env(
pay(gw, self, t.btcStart));
4429 env(
pay(gw, self, usd(2'000)));
4430 if (self.
id() != leg1.
id())
4431 env(
pay(gw, leg1, usd(2'000)));
4436 env(
offer(leg0, btc(100),
XRP(100'000), tfPassive));
4440 env(
offer(leg1,
XRP(100'000), usd(1'000), tfPassive));
4445 env(
offer(self, usd(1'000), btc(100)));
4450 for (
auto const& actor : t.actors)
4456 actorOffers.begin(),
4458 return (*offer)[sfTakerGets].signum() == 0;
4460 BEAST_EXPECT(offerCount == actor.offers);
4482 testcase(
"Self Pay Unlimited Funds");
4514 using namespace jtx;
4515 auto const gw =
Account(
"gw");
4517 auto test = [&](
auto&& issue1,
auto&& issue2) {
4518 Env env{*
this, features};
4519 auto const baseFee = env.
current()->fees().base.drops();
4521 auto const startXrpBalance =
XRP(4'000'000);
4523 env.
fund(startXrpBalance, gw);
4526 auto const btc = issue1(
4527 {.env = env, .token =
"BTC", .issuer = gw, .
limit = 40, .transferFee = 25'000});
4529 auto const usd = issue2(
4530 {.env = env, .token =
"USD", .issuer = gw, .
limit = 8'000, .transferFee = 25'000});
4557 TestData
const flowTests[]{
4559 {0, 0, 1, btc(5), {{
"gay", 1,
drops(3950000'000000 - (4 * baseFee)), btc(5), usd (2500)}, {
"gar", 1,
drops(4050000'000000 - (3 * baseFee)), btc(0), usd(1375)}}},
4560 {0, 0, 0, btc(5), {{
"hye", 2,
drops(4000000'000000 - (5 * baseFee)), btc(5), usd (2000)} }}
4564 for (
auto const& t : flowTests)
4566 Account const& self = t.actors[t.self].acct;
4567 Account const& leg0 = t.actors[t.leg0].acct;
4568 Account const& leg1 = t.actors[t.leg1].acct;
4570 for (
auto const& actor : t.actors)
4572 env.
fund(
XRP(4'000'000), actor.acct);
4578 mbtc.authorize({.account = actor.acct});
4582 env(
trust(actor.acct, btc(40)));
4588 musd.authorize({.account = actor.acct});
4592 env(
trust(actor.acct, usd(8'000)));
4597 env(
pay(gw, self, t.btcStart));
4598 env(
pay(gw, self, usd(2'000)));
4599 if (self.
id() != leg1.
id())
4600 env(
pay(gw, leg1, usd(2'000)));
4605 env(
offer(leg0, btc(10),
XRP(100'000), tfPassive));
4609 env(
offer(leg1,
XRP(100'000), usd(1'000), tfPassive));
4614 env(
offer(self, usd(1'000), btc(10)));
4619 for (
auto const& actor : t.actors)
4625 actorOffers.begin(),
4627 return (*offer)[sfTakerGets].signum() == 0;
4629 BEAST_EXPECT(offerCount == actor.offers);
4653 using namespace jtx;
4655 Env env{*
this, features};
4657 auto const gw =
Account(
"gw");
4658 auto const alice =
Account(
"alice");
4659 auto const bob =
Account(
"bob");
4661 env.
fund(
XRP(400'000), gw, alice, bob);
4667 MPT const gwUSD = gwMUSD;
4670 gwMUSD.authorize({.account = alice});
4671 gwMUSD.authorize({.account = gw, .holder = alice});
4672 gwMUSD.authorize({.account = bob});
4673 gwMUSD.authorize({.account = gw, .holder = bob});
4675 env(
offer(alice, gwUSD(40),
XRP(4'000)));
4681 env(
pay(gw, bob, gwUSD(50)));
4687 env(
offer(bob,
XRP(4'000), gwUSD(40)));
4713 using namespace jtx;
4715 Env env{*
this, features};
4717 auto const gw =
Account(
"gw");
4718 auto const alice =
Account(
"alice");
4719 auto const bob =
Account(
"bob");
4721 env.
fund(
XRP(400'000), gw, alice, bob);
4726 MPT const gwUSD = gwMUSD;
4736 gwMUSD.authorize({.account = bob});
4737 gwMUSD.authorize({.account = gw, .holder = bob});
4739 env(
pay(gw, bob, gwUSD(50)));
4745 env(
offer(bob,
XRP(4'000), gwUSD(40)));
4753 gwMUSD.authorize({.account = alice});
4771 gwMUSD.authorize({.account = gw, .holder = alice});
4773 env(
offer(alice, gwUSD(40),
XRP(4'000)));
4779 env(
offer(bob,
XRP(4'000), gwUSD(40)));
4794 using namespace jtx;
4796 Env env{*
this, features};
4798 auto const gw =
Account(
"gw");
4799 auto const alice =
Account(
"alice");
4801 env.
fund(
XRP(400'000), gw, alice);
4806 MPT const gwUSD = gwMUSD;
4809 env(
offer(gw, gwUSD(40),
XRP(4'000)));
4822 env(
offer(gw, gwUSD(40),
XRP(4'000)));
4830 gwMUSD.authorize({.account = alice});
4831 gwMUSD.authorize({.account = gw, .holder = alice});
4833 env(
pay(gw, alice, gwUSD(50)));
4839 env(
offer(alice,
XRP(4'000), gwUSD(40)));
4852 using namespace jtx;
4856 auto mpTokenExists =
4866 Env env{*
this, features};
4870 auto musd =
MPTTester({.env = env, .issuer = gw});
4871 MPT const usd = musd;
4873 musd.authorize({.account = becky});
4874 BEAST_EXPECT(mpTokenExists(env, becky, usd.issuanceID));
4875 env(
pay(gw, becky, usd(5)));
4878 auto mbux =
MPTTester({.env = env, .issuer = alice});
4879 MPT const bux = mbux;
4890 env(
pay(becky, gw, usd(5)));
4891 musd.authorize({.account = becky, .flags = tfMPTUnauthorize});
4893 BEAST_EXPECT(!mpTokenExists(env, becky, usd.issuanceID));
4894 BEAST_EXPECT(
isOffer(env, becky,
XRP(2), usd(2)));
4895 BEAST_EXPECT(
isOffer(env, becky, bux(3), usd(3)));
4905 int const delta = [&env, &gw, openLedgerSeq = env.
current()->seq()]() ->
int {
4907 if (gwSeq + 255 > openLedgerSeq)
4908 return gwSeq - openLedgerSeq + 255;
4912 for (
int i = 0; i < delta; ++i)
4928 BEAST_EXPECT(
isOffer(env, becky,
XRP(2), usd(2)));
4929 BEAST_EXPECT(
isOffer(env, becky, bux(3), usd(3)));
4935 BEAST_EXPECT(
isOffer(env, becky,
XRP(2), usd(2)));
4936 BEAST_EXPECT(
isOffer(env, becky, bux(3), usd(3)));
4941 BEAST_EXPECT(!
isOffer(env, becky, bux(3), usd(3)));
4945 mbux.authorize({.account = carol});
4946 env(
pay(alice, carol, bux(2)));
4955 BEAST_EXPECT(
isOffer(env, alice, bux(2),
XRP(2)));
4956 BEAST_EXPECT(
isOffer(env, becky,
XRP(2), usd(2)));
4965 return (*rhs)[sfSequence] < (*lhs)[sfSequence];
4975 using namespace jtx;
4983 Env env{*
this, features};
4984 auto const gw =
Account{
"gateway"};
4985 auto const alice =
Account{
"alice"};
4986 auto const bob =
Account{
"bob"};
4988 env.
fund(
XRP(10'000), gw, alice, bob);
4991 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
4993 env(
pay(gw, alice, usd(200)));
5000 env(
offer(alice,
XRP(50), usd(50)));
5010 BEAST_EXPECT(offerId1 == offerId0 + 4);
5011 env(
offer(alice,
XRP(50), usd(50)));
5027 BEAST_EXPECT(offers.size() == 4);
5028 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId0);
5029 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId3);
5030 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId2);
5031 BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerId1);
5043 BEAST_EXPECT(offers.size() == 3);
5044 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId3);
5045 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId2);
5046 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId1);
5056 BEAST_EXPECT(offers.size() == 2);
5057 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId3);
5058 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId2);
5068 BEAST_EXPECT(offers.size() == 1);
5069 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId3);
5079 BEAST_EXPECT(offers.empty());
5092 using namespace jtx;
5096 Env env{*
this, features};
5097 auto const gw =
Account{
"gateway"};
5098 auto const alice =
Account{
"alice"};
5100 env.
fund(
XRP(10'000), gw, alice);
5103 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice}});
5107 env(
pay(gw, alice, usd(200)));
5112 env(
offer(alice,
XRP(50), usd(50)));
5124 BEAST_EXPECT(offerSeqId1 == offerSeqId0 + 6);
5125 env(
offer(alice,
XRP(50), usd(50)));
5141 BEAST_EXPECT(offers.size() == 4);
5142 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId0);
5143 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId1);
5144 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerTixId0);
5145 BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerSeqId1);
5157 BEAST_EXPECT(offers.size() == 3);
5158 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId1);
5159 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId0);
5160 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerSeqId1);
5170 BEAST_EXPECT(offers.size() == 2);
5171 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId1);
5172 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerSeqId1);
5185 BEAST_EXPECT(offers.size() == 1);
5186 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId1);
5202 using namespace jtx;
5203 Account const issuer(
"issuer");
5207 auto test = [&](
auto&& issue1,
auto&& issue2) {
5208 Env env(*
this, features);
5211 env.
fund(
XRP(1'000), maker, taker);
5215 issue1({.env = env, .token =
"USD", .issuer = issuer, .holders = {maker, taker}});
5217 issue2({.env = env, .token =
"EUR", .issuer = issuer, .holders = {maker, taker}});
5219 env(
pay(issuer, maker, usd(1'000)));
5220 env(
pay(issuer, taker, usd(1'000)));
5221 env(
pay(issuer, maker, eur(1'000)));
5224 auto makerUSDBalance = env.
balance(maker, usd).
value();
5225 auto takerUSDBalance = env.
balance(taker, usd).
value();
5226 auto makerEURBalance = env.
balance(maker, eur).
value();
5227 auto takerEURBalance = env.
balance(taker, eur).
value();
5235 env(
offer(maker,
XRP(100), usd(100)));
5241 makerXRPBalance -=
txFee(env, 1);
5242 takerXRPBalance -=
txFee(env, 1);
5245 makerUSDBalance -= usd(100);
5246 takerUSDBalance += usd(100);
5247 makerXRPBalance +=
XRP(100).value();
5248 takerXRPBalance -=
XRP(100).value();
5252 env(
offer(maker, usd(100),
XRP(100)));
5258 makerXRPBalance -=
txFee(env, 1);
5259 takerXRPBalance -=
txFee(env, 1);
5262 makerUSDBalance += usd(100);
5263 takerUSDBalance -= usd(100);
5264 makerXRPBalance -=
XRP(100).value();
5265 takerXRPBalance +=
XRP(100).value();
5269 env(
offer(maker, usd(100), eur(100)));
5272 env(
offer(taker, eur(100), usd(101)),
Txflags(tfFillOrKill),
Ter(err));
5275 makerXRPBalance -=
txFee(env, 1);
5276 takerXRPBalance -=
txFee(env, 1);
5279 makerUSDBalance += usd(100);
5280 takerUSDBalance -= usd(100);
5281 makerEURBalance -= eur(100);
5282 takerEURBalance += eur(100);
5289 env(
offer(maker,
XRP(101), usd(101)));
5292 env(
offer(taker, usd(100),
XRP(101)),
Txflags(tfFillOrKill | tfSell));
5295 makerUSDBalance -= usd(101);
5296 takerUSDBalance += usd(101);
5297 makerXRPBalance +=
XRP(101).value() -
txFee(env, 1);
5298 takerXRPBalance -=
XRP(101).value() +
txFee(env, 1);
5301 env(
offer(maker, usd(101),
XRP(101)));
5304 env(
offer(taker,
XRP(100), usd(101)),
Txflags(tfFillOrKill | tfSell));
5307 makerUSDBalance += usd(101);
5308 takerUSDBalance -= usd(101);
5309 makerXRPBalance -=
XRP(101).value() +
txFee(env, 1);
5310 takerXRPBalance +=
XRP(101).value() -
txFee(env, 1);
5313 env(
offer(maker, usd(101), eur(101)));
5316 env(
offer(taker, eur(100), usd(101)),
Txflags(tfFillOrKill | tfSell));
5319 makerUSDBalance += usd(101);
5320 takerUSDBalance -= usd(101);
5321 makerEURBalance -= eur(101);
5322 takerEURBalance += eur(101);
5323 makerXRPBalance -=
txFee(env, 1);
5324 takerXRPBalance -=
txFee(env, 1);
5329 for (
auto const flags : {tfFillOrKill, tfFillOrKill + tfSell})
5331 env(
offer(maker,
XRP(100), usd(100)));
5337 makerXRPBalance -=
txFee(env, 1);
5338 takerXRPBalance -=
txFee(env, 1);
5341 env(
offer(maker, usd(100),
XRP(100)));
5347 makerXRPBalance -=
txFee(env, 1);
5348 takerXRPBalance -=
txFee(env, 1);
5351 env(
offer(maker, usd(100), eur(100)));
5357 makerXRPBalance -=
txFee(env, 1);
5358 takerXRPBalance -=
txFee(env, 1);
5363 env.
balance(maker, usd) == makerUSDBalance &&
5364 env.
balance(taker, usd) == takerUSDBalance &&
5365 env.
balance(maker, eur) == makerEURBalance &&
5366 env.
balance(taker, eur) == takerEURBalance &&
5367 env.
balance(maker,
XRP) == makerXRPBalance &&
5378 using namespace jtx;
5380 auto const gw =
Account{
"gateway"};
5381 auto const alice =
Account{
"alice"};
5386 auto const iou = gw[name];
5387 env(
trust(alice, iou(1'000)));
5388 env(
pay(gw, alice, iou(100)));
5394 MPTTester({.env = env, .issuer = gw, .holders = {alice}, .pay = 1'000'000'000});
5411 {.toAsset1 = getIOU, .toAsset2 = getIOU, .val1 = 10, .val2 = 30},
5412 {.toAsset1 = getIOU, .toAsset2 = getXRP, .val1 = 10, .val2 = 30'000'000},
5413 {.toAsset1 = getXRP, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30},
5414 {.toAsset1 = getMPT, .toAsset2 = getXRP, .val1 = 10'000'000, .val2 = 30'000'000},
5415 {.toAsset1 = getXRP, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000},
5416 {.toAsset1 = getIOU, .toAsset2 = getMPT, .val1 = 10, .val2 = 30'000'000},
5417 {.toAsset1 = getMPT, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30},
5418 {.toAsset1 = getMPT, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}};
5419 for (TestInfo
const& t :
tests)
5421 Env env{*
this, features};
5422 env.
fund(
XRP(10'000), gw, alice);
5425 auto const xts = t.toAsset1(env);
5426 auto const xxx = t.toAsset2(env);
5429 return asset.raw().visit(
5430 [&](
Issue const& issue) {
return issue.
native() ?
"XRPIssue" :
"Issue"; },
5431 [&](
MPTIssue const&) {
return "MPTIssue"; });
5434 testcase <<
"offer: " << tokenType(xts) <<
"/" << tokenType(xxx);
5438 auto txn =
noop(gw);
5439 txn[sfTickSize.fieldName] = 5;
5441 BEAST_EXPECT((*env.
le(gw))[sfTickSize] == 5);
5444 env(
offer(alice, xts(t.val1), xxx(t.val2)));
5445 env(
offer(alice, xts(t.val2), xxx(t.val1)));
5446 env(
offer(alice, xts(t.val1), xxx(t.val2)),
Json(jss::Flags, tfSell));
5447 env(
offer(alice, xts(t.val2), xxx(t.val1)),
Json(jss::Flags, tfSell));
5451 if (sle->getType() == ltOFFER)
5455 std::make_pair((*sle)[sfTakerPays], (*sle)[sfTakerGets]));
5460 auto it = offers.begin();
5461 BEAST_EXPECT(it != offers.end());
5462 if (xxx.native() && !xts.holds<
MPTIssue>())
5465 it->second.first == xts(t.val1) && it->second.second ==
XRPAmount(29'999'400));
5467 else if (!xxx.integral())
5470 it->second.first == xts(t.val1) && it->second.second < xxx(t.val2) &&
5471 it->second.second >
STAmount(xxx, 29'9994, -4));
5475 BEAST_EXPECT(it->second.first == xts(t.val1) && it->second.second == xxx(t.val2));
5480 BEAST_EXPECT(it != offers.end());
5481 BEAST_EXPECT(it->second.first == xts(t.val2) && it->second.second == xxx(t.val1));
5485 BEAST_EXPECT(it != offers.end());
5486 if (xts.native() && !xxx.holds<
MPTIssue>())
5489 it->second.first ==
XRPAmount(10'000'200) && it->second.second == xxx(t.val2));
5491 else if (!xts.integral())
5494 it->second.first ==
STAmount(xts, 10'0002, -4) &&
5495 it->second.second == xxx(t.val2));
5499 BEAST_EXPECT(it->second.first == xts(t.val1) && it->second.second == xxx(t.val2));
5505 BEAST_EXPECT(it != offers.end());
5506 BEAST_EXPECT(it->second.first == xts(t.val2) && it->second.second == xxx(t.val1));
5508 BEAST_EXPECT(++it == offers.end());
5534 using namespace jtx;
5538 auto const kBigMpt = 5'000'000'000'000'000'000LL;
5540 auto runScenario = [&](
bool withTickSize) {
5541 Env env{*
this, features};
5542 auto const gw =
Account{
"gateway"};
5543 auto const alice =
Account{
"alice"};
5544 env.
fund(
XRP(10'000), gw, alice);
5547 auto const usd = gw[
"USD"];
5548 env(
trust(alice, usd(1'000)));
5549 env(
pay(gw, alice, usd(100)));
5554 auto txn =
noop(gw);
5555 txn[sfTickSize.fieldName] = 5;
5558 BEAST_EXPECT((*env.
le(gw))[sfTickSize] == 5);
5569 BEAST_EXPECT(
getRate(usd(1), mpt(kBigMpt)) == 0);
5576 BEAST_EXPECT(
getRate(usd(1), mpt(kBigMpt)) == 0);
5610 testcase(
"Zero Rate XRP/IOU Offer");
5612 using namespace jtx;
5614 auto const gw =
Account{
"gateway"};
5615 auto const alice =
Account{
"alice"};
5616 auto const bob =
Account{
"bob"};
5617 auto const usd = gw[
"USD"];
5622 auto const tinyUsd =
STAmount{usd, UINT64_C(1'000'000'000'000'000), -96};
5624 auto setup = [&](
Env& env) {
5625 env.fund(
XRP(100'000), gw, alice, bob);
5627 env(
trust(alice, usd(1'000)));
5628 env(
trust(bob, usd(1'000)));
5629 env(
pay(gw, bob, usd(100)));
5635 Env env{*
this, features - featureMPTokensV2};
5639 BEAST_EXPECT(
getRate(
XRP(1'000), tinyUsd) == 0);
5644 BEAST_EXPECT(aliceOffers.size() == 1);
5646 BEAST_EXPECT(
getQuality((*aliceOffers.front())[sfBookDirectory]) == 0);
5663 Env env{*
this, features - featureMPTokensV2};
5666 auto txn =
noop(gw);
5667 txn[sfTickSize.fieldName] = 5;
5670 BEAST_EXPECT((*env.
le(gw))[sfTickSize] == 5);
5672 BEAST_EXPECT(
getRate(
XRP(1'000), tinyUsd) == 0);
5681 Env env{*
this, features};
5684 BEAST_EXPECT(
getRate(
XRP(1000), tinyUsd) == 0);
5696 Env env{*
this, features};
5700 auto const bobSeq = env.
seq(bob);
5707 BEAST_EXPECT(
getRate(
XRP(1'000), tinyUsd) == 0);
5715 BEAST_EXPECT(env.
balance(alice, usd).
value() == tinyUsd);
5732 testcase(
"Auto-Create Object Without Reserve Check During Partial Crossing");
5734 using namespace jtx;
5736 auto const gw =
Account{
"gateway"};
5737 auto const alice =
Account{
"alice"};
5738 auto const carol =
Account{
"carol"};
5739 auto const bob =
Account{
"bob"};
5741 auto test = [&](
auto&& getToken,
auto&& execTx) {
5746 Env env{*
this, features};
5748 auto const f = env.
current()->fees().base;
5749 auto const r =
reserve(env, 0);
5750 auto const inc =
reserve(env, 1) - r;
5752 env.
fund(
XRP(10'000), gw, alice, bob);
5757 auto const token = getToken(env);
5763 if (BEAST_EXPECT(inc >
XRP(30)))
5764 env.
fund(r + inc + f +
XRP(30), carol);
5783 auto const carolBalance = r + inc +
XRP(15);
5785 BEAST_EXPECT(carolBalance < r + 2 * inc);
5788 env.trust(gw[
"USD"](1'000), alice);
5789 env(
pay(gw, alice, gw[
"USD"](100)));
5793 MPT const mpT1 =
MPTTester({.env = env, .issuer = gw, .holders = {alice}, .pay = 100});
5796 for (
auto&& getToken : {getIOU, getMPT})
5808 env(
pay(alice, bob,
XRP(15)),
5811 Txflags(tfNoRippleDirect | tfPartialPayment));
5819 testcase(
"book_offers uses MPT issuer capacity, transfer fees, and locks");
5821 using namespace jtx;
5823 Account const issuer{
"issuer"};
5831 Env env{*
this, features};
5833 env.
fund(
XRP(10'000), issuer, maker, buyer);
5837 {.env = env, .issuer = issuer, .holders = {maker, buyer}, .maxAmt = 100});
5838 MPT const usd = musd;
5840 auto const issuerOfferSeq = env.
seq(issuer);
5841 env(
offer(issuer,
XRP(100), usd(100)));
5843 musd.pay(issuer, maker, 100);
5846 if (!BEAST_EXPECT(issuance))
5848 BEAST_EXPECT(issuance->getFieldU64(sfOutstandingAmount) == 100);
5849 BEAST_EXPECT(issuance->getFieldU64(sfMaximumAmount) == 100);
5851 env(
offer(maker,
XRP(200), usd(100)));
5855 BEAST_EXPECT(bookOffers.
isArray());
5856 if (!BEAST_EXPECT(bookOffers.
size() >= 2))
5860 BEAST_EXPECT(issuerOffer[sfAccount.jsonName] == issuer.
human());
5861 BEAST_EXPECT(issuerOffer[sfSequence.jsonName] == issuerOfferSeq);
5862 BEAST_EXPECT(issuerOffer[jss::owner_funds] ==
"0");
5863 BEAST_EXPECT(issuerOffer.
isMember(jss::taker_gets_funded));
5864 BEAST_EXPECT(issuerOffer[jss::taker_gets_funded][jss::value] ==
"0");
5865 BEAST_EXPECT(issuerOffer.
isMember(jss::taker_pays_funded));
5866 BEAST_EXPECT(issuerOffer[jss::taker_pays_funded] ==
"0");
5873 Env env{*
this, features};
5875 env.
fund(
XRP(10'000), issuer, buyer);
5878 MPTTester const musd({.env = env, .issuer = issuer, .holders = {buyer}, .maxAmt = 150});
5879 MPT const usd = musd;
5881 auto const firstIssuerOfferSeq = env.
seq(issuer);
5882 env(
offer(issuer,
XRP(100), usd(100)));
5883 auto const secondIssuerOfferSeq = env.
seq(issuer);
5884 env(
offer(issuer,
XRP(100), usd(100)));
5888 BEAST_EXPECT(bookOffers.
isArray());
5889 if (!BEAST_EXPECT(bookOffers.
size() >= 2))
5893 BEAST_EXPECT(firstOffer[sfAccount.jsonName] == issuer.
human());
5894 BEAST_EXPECT(firstOffer[sfSequence.jsonName] == firstIssuerOfferSeq);
5895 BEAST_EXPECT(firstOffer[jss::owner_funds] ==
"150");
5896 BEAST_EXPECT(!firstOffer.
isMember(jss::taker_gets_funded));
5897 BEAST_EXPECT(!firstOffer.
isMember(jss::taker_pays_funded));
5900 BEAST_EXPECT(secondOffer[sfAccount.jsonName] == issuer.
human());
5901 BEAST_EXPECT(secondOffer[sfSequence.jsonName] == secondIssuerOfferSeq);
5902 BEAST_EXPECT(!secondOffer.
isMember(jss::owner_funds));
5903 BEAST_EXPECT(secondOffer.
isMember(jss::taker_gets_funded));
5904 BEAST_EXPECT(secondOffer[jss::taker_gets_funded][jss::value] ==
"50");
5905 BEAST_EXPECT(secondOffer.
isMember(jss::taker_pays_funded));
5906 BEAST_EXPECT(secondOffer[jss::taker_pays_funded] ==
"50000000");
5909 auto checkTransferFeeBookOffers = [&](
std::uint16_t transferFee,
auto&& checkOffers) {
5910 Env env{*
this, features};
5912 env.
fund(
XRP(10'000), issuer, maker, buyer);
5918 .holders = {maker, buyer},
5919 .transferFee = transferFee,
5921 MPT const usd = musd;
5922 if (transferFee != 0)
5923 BEAST_EXPECT(musd.checkTransferFee(transferFee));
5925 auto const firstOfferSeq = env.
seq(maker);
5926 env(
offer(maker,
XRP(1'500), usd(1'500)));
5927 auto const secondOfferSeq = env.
seq(maker);
5928 env(
offer(maker,
XRP(1'500), usd(1'500)));
5932 BEAST_EXPECT(bookOffers.
isArray());
5933 if (!BEAST_EXPECT(bookOffers.
size() == 2))
5936 checkOffers(bookOffers, firstOfferSeq, secondOfferSeq);
5941 checkTransferFeeBookOffers(
5946 for (
auto const i : {0u, 1u})
5949 BEAST_EXPECT(
offer[sfAccount.jsonName] == maker.
human());
5951 offer[sfSequence.jsonName] == (i == 0u ? firstOfferSeq : secondOfferSeq));
5952 BEAST_EXPECT(!
offer.isMember(jss::taker_gets_funded));
5953 BEAST_EXPECT(!
offer.isMember(jss::taker_pays_funded));
5955 BEAST_EXPECT(bookOffers[0u][jss::owner_funds] ==
"3000");
5960 checkTransferFeeBookOffers(
5966 BEAST_EXPECT(firstOffer[sfAccount.jsonName] == maker.
human());
5967 BEAST_EXPECT(firstOffer[sfSequence.jsonName] == firstOfferSeq);
5968 BEAST_EXPECT(firstOffer[jss::owner_funds] ==
"3000");
5969 BEAST_EXPECT(!firstOffer.
isMember(jss::taker_gets_funded));
5970 BEAST_EXPECT(!firstOffer.
isMember(jss::taker_pays_funded));
5973 BEAST_EXPECT(secondOffer[sfAccount.jsonName] == maker.
human());
5974 BEAST_EXPECT(secondOffer[sfSequence.jsonName] == secondOfferSeq);
5979 BEAST_EXPECT(secondOffer.
isMember(jss::taker_gets_funded));
5980 BEAST_EXPECT(secondOffer[jss::taker_gets_funded][jss::value] ==
"500");
5981 BEAST_EXPECT(secondOffer.
isMember(jss::taker_pays_funded));
5982 BEAST_EXPECT(secondOffer[jss::taker_pays_funded] ==
"500000000");
5991 Env env{*
this, features};
5992 env.
fund(
XRP(10'000), issuer, maker, buyer);
5998 .holders = {maker, buyer},
6002 auto const firstOfferSeq = env.
seq(maker);
6004 auto const secondOfferSeq = env.
seq(maker);
6010 BEAST_EXPECT(bookOffers.
isArray());
6011 if (BEAST_EXPECT(bookOffers.
size() == 2))
6014 BEAST_EXPECT(firstOffer[sfSequence.jsonName] == firstOfferSeq);
6015 BEAST_EXPECT(firstOffer[jss::owner_funds] ==
"3");
6016 BEAST_EXPECT(!firstOffer.
isMember(jss::taker_gets_funded));
6017 BEAST_EXPECT(!firstOffer.
isMember(jss::taker_pays_funded));
6020 BEAST_EXPECT(secondOffer[sfSequence.jsonName] == secondOfferSeq);
6021 BEAST_EXPECT(secondOffer.
isMember(jss::taker_gets_funded));
6022 BEAST_EXPECT(secondOffer[jss::taker_gets_funded][jss::value] ==
"0");
6023 BEAST_EXPECT(secondOffer.
isMember(jss::taker_pays_funded));
6024 BEAST_EXPECT(secondOffer[jss::taker_pays_funded] ==
"0");
6032 BEAST_EXPECT(env.
balance(buyer, usd) == usd(3 + 1));
6033 BEAST_EXPECT(env.
balance(maker, usd) == usd(3 - 2));
6046 auto checkLargeOwnerFunds =
6048 Env env{*
this, features};
6049 env.
fund(
XRP(10'000), issuer, maker, buyer);
6055 .holders = {maker, buyer},
6056 .transferFee = transferFee,
6058 env(
pay(issuer, maker, usd(funds)));
6061 auto const offerSeq = env.
seq(maker);
6062 env(
offer(maker,
XRP(100), usd(funds)));
6066 BEAST_EXPECT(!jrr.
isMember(jss::error));
6068 BEAST_EXPECT(bookOffers.
isArray());
6069 if (!BEAST_EXPECT(bookOffers.
size() == 1))
6073 BEAST_EXPECT(
offer[sfAccount.jsonName] == maker.
human());
6074 BEAST_EXPECT(
offer[sfSequence.jsonName] == offerSeq);
6076 BEAST_EXPECT(
offer[jss::taker_gets_funded][jss::value] == expectedFunded);
6081 checkLargeOwnerFunds(
kMaxTransferFee, 300'000'000'000'000'000LL,
"200000000000000000");
6085 checkLargeOwnerFunds(
kMaxTransferFee, 6'000'000'000'000'000'000LL,
"4000000000000000000");
6090 checkLargeOwnerFunds(1, 9'000'000'000'000'000'000LL,
"8999910000899991000");
6095 Env env{*
this, features};
6097 env.
fund(
XRP(10'000), issuer, maker, buyer);
6103 .holders = {maker, buyer},
6106 MPT const usd = musd;
6108 auto const offerSeq = env.
seq(maker);
6109 env(
offer(maker,
XRP(100), usd(100)));
6115 BEAST_EXPECT(bookOffers.
isArray());
6116 if (!BEAST_EXPECT(bookOffers.
size() == 1))
6120 BEAST_EXPECT(
offer[sfAccount.jsonName] == maker.
human());
6121 BEAST_EXPECT(
offer[sfSequence.jsonName] == offerSeq);
6122 BEAST_EXPECT(
offer[jss::owner_funds] ==
"100");
6123 BEAST_EXPECT(!
offer.isMember(jss::taker_gets_funded));
6124 BEAST_EXPECT(!
offer.isMember(jss::taker_pays_funded));
6127 musd.set({.flags = tfMPTLock});
6133 BEAST_EXPECT(bookOffers.
isArray() && bookOffers.
size() == 1);
6136 BEAST_EXPECT(
offer[sfAccount] == maker.
human());
6137 BEAST_EXPECT(
offer[sfSequence] == offerSeq);
6138 BEAST_EXPECT(
offer[jss::owner_funds] ==
"0");
6139 BEAST_EXPECT(
offer.isMember(jss::taker_gets_funded));
6140 BEAST_EXPECT(
offer[jss::taker_gets_funded][jss::value] ==
"0");
6141 BEAST_EXPECT(
offer.isMember(jss::taker_pays_funded));
6142 BEAST_EXPECT(
offer[jss::taker_pays_funded] ==
"0");
6156 testcase(
"getBookBase: (Issue,MPT) vs (MPT,Issue) preimage collision");
6168 BEAST_EXPECT(issuerB.
parseHex(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA00000007"));
6169 BEAST_EXPECT(issuerA.
parseHex(
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"));
6170 BEAST_EXPECT(currencyB.
parseHex(
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"));
6171 BEAST_EXPECT(currencyA.
parseHex(
"00000005AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
6172 BEAST_EXPECT(sharedIOUIssuer.
parseHex(
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"));
6183 BEAST_EXPECT(bookA != bookB);
6193 testcase(
"getBookBase: (MPT,MPT) distinguishes from mixed branches");
6199 BEAST_EXPECT(issuerX.
parseHex(
"1111111111111111111111111111111111111111"));
6200 BEAST_EXPECT(issuerY.
parseHex(
"2222222222222222222222222222222222222222"));
6201 BEAST_EXPECT(currency.
parseHex(
"3333333333333333333333333333333333333333"));
6202 BEAST_EXPECT(iouIssuer.
parseHex(
"4444444444444444444444444444444444444444"));
6206 Book const mptBook{mptX, mptY, std::nullopt};
6207 Book const mixedBook{mptX,
Asset{
Issue{currency, iouIssuer}}, std::nullopt};
6208 Book const reversedMptBook{mptY, mptX, std::nullopt};
6217 testcase(
"getBookBase: domain does not reopen MPT preimage collisions");
6224 BEAST_EXPECT(issuerX.
parseHex(
"1111111111111111111111111111111111111111"));
6225 BEAST_EXPECT(issuerY.
parseHex(
"2222222222222222222222222222222222222222"));
6226 BEAST_EXPECT(currency.
parseHex(
"3333333333333333333333333333333333333333"));
6227 BEAST_EXPECT(iouIssuer.
parseHex(
"4444444444444444444444444444444444444444"));
6230 "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
6231 "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD");
6237 Book const publicBook{mptX, iou, std::nullopt};
6238 Book const domainBook{mptX, iou, domainA};
6243 Book const mi{mptX, iou, domainA};
6244 Book const im{iou, mptX, domainA};
6245 BEAST_EXPECT(mi != im);
6263 testcase(
"MPT Offer Zero Rate - crossable quality");
6265 using namespace jtx;
6271 auto const kBigMpt = 200'000'000'000'000'000LL;
6277 auto runScenario = [&](
STAmount const& aliceQuote,
bool rateRepresentable) {
6278 Env env{*
this, features};
6279 auto const gw =
Account{
"gateway"};
6280 auto const alice =
Account{
"alice"};
6281 auto const bob =
Account{
"bob"};
6282 env.
fund(
XRP(10'000), gw, alice, bob);
6286 {.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt =
kMaxMpTokenAmount});
6288 env(
pay(gw, bob, mpt(kBigMpt)));
6295 BEAST_EXPECT(
getRate(mpt(kBigMpt),
XRP(1)) != 0);
6296 auto const bobSeq = env.
seq(bob);
6303 BEAST_EXPECT((
getRate(aliceQuote, mpt(kBigMpt)) != 0) == rateRepresentable);
6312 BEAST_EXPECT(env.
balance(alice, mpt) == mpt(kBigMpt));
6321 runScenario(
XRP(1),
false);
6326 runScenario(
XRP(2),
true);
6337 testcase(
"MPT Offer Zero Rate - partial cross");
6339 using namespace jtx;
6341 auto const kBigMpt = 200'000'000'000'000'000LL;
6343 Env env{*
this, features};
6344 auto const gw =
Account{
"gateway"};
6345 auto const alice =
Account{
"alice"};
6346 auto const bob =
Account{
"bob"};
6347 env.
fund(
XRP(10'000), gw, alice, bob);
6351 {.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt =
kMaxMpTokenAmount});
6353 env(
pay(gw, bob, mpt(kBigMpt)));
6357 auto const bobSeq = env.
seq(bob);
6364 BEAST_EXPECT(
getRate(
XRP(2), mpt(2 * kBigMpt)) == 0);
6367 auto const fee = env.
current()->fees().base;
6373 BEAST_EXPECT(env.
balance(alice, mpt) == mpt(kBigMpt));
6380 BEAST_EXPECT((*env.
le(alice))[sfOwnerCount] == 1);
6392 testcase(
"MPT Offer Zero Rate - tick size with crossing");
6394 using namespace jtx;
6396 auto const kBigMpt = 5'000'000'000'000'000'000LL;
6398 Env env{*
this, features};
6399 auto const gw =
Account{
"gateway"};
6400 auto const alice =
Account{
"alice"};
6401 auto const bob =
Account{
"bob"};
6402 env.
fund(
XRP(10'000), gw, alice, bob);
6405 auto const usd = gw[
"USD"];
6406 env(
trust(alice, usd(1'000)));
6407 env(
pay(gw, alice, usd(100)));
6410 auto txn =
noop(gw);
6411 txn[sfTickSize.fieldName] = 5;
6414 BEAST_EXPECT((*env.
le(gw))[sfTickSize] == 5);
6417 {.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt =
kMaxMpTokenAmount});
6418 env(
pay(gw, bob, mpt(kBigMpt)));
6422 BEAST_EXPECT(
getRate(mpt(kBigMpt), usd(1)) != 0);
6423 auto const bobSeq = env.
seq(bob);
6430 BEAST_EXPECT(
getRate(usd(1), mpt(kBigMpt)) == 0);
6434 BEAST_EXPECT(env.
balance(alice, mpt) == mpt(kBigMpt));
6451 testcase(
"MPT Offer Zero Rate - IOC and FoK");
6453 using namespace jtx;
6455 auto const kBigMpt = 200'000'000'000'000'000LL;
6458 Env env{*
this, features};
6459 auto const gw =
Account{
"gateway"};
6460 auto const alice =
Account{
"alice"};
6461 auto const bob =
Account{
"bob"};
6462 env.
fund(
XRP(10'000), gw, alice, bob);
6466 {.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt =
kMaxMpTokenAmount});
6467 env(
pay(gw, bob, mpt(kBigMpt)));
6470 auto const bobSeq = env.
seq(bob);
6476 BEAST_EXPECT(
getRate(
XRP(2), mpt(2 * kBigMpt)) == 0);
6477 env(
offer(alice, mpt(2 * kBigMpt),
XRP(2), flags),
Ter(expected));
6480 auto const bobOfferLive =
6486 BEAST_EXPECT(env.
balance(alice, mpt) == mpt(kBigMpt));
6487 BEAST_EXPECT(!bobOfferLive);
6493 BEAST_EXPECT(env.
balance(alice, mpt) == mpt(0));
6494 BEAST_EXPECT(bobOfferLive);
6499 runScenario(tfImmediateOrCancel,
tesSUCCESS);
6579 using namespace jtx;
TestcaseT testcase
Memberspace for declaring test cases.
UInt size() const
Number of values in array or object.
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
static BaseUInt fromVoid(void const *data)
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
A currency issued by an account.
std::string getText() const override
std::shared_ptr< STLedgerEntry const > const & ConstRef
std::shared_ptr< STLedgerEntry const > const_pointer
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
constexpr value_type value() const
Returns the underlying value.
void testCurrencyConversionInParts(FeatureBitset features)
void run() override
Runs the suite.
void testCurrencyConversionEntire(FeatureBitset features)
void testXRPDirectCross(FeatureBitset features)
void testSellFlagExceedLimit(FeatureBitset features)
void testSelfPayUnlimitedFunds(FeatureBitset features)
void testCanceledOffer(FeatureBitset features)
void testMalformed(FeatureBitset features)
static std::vector< SLE::const_pointer > offersOnAccount(jtx::Env &env, jtx::Account account)
void testOfferAcceptThenCancel(FeatureBitset features)
static std::uint32_t lastClose(jtx::Env &env)
void testOfferInScalingWithXferRate(FeatureBitset features)
void testRequireAuth(FeatureBitset features)
void testUnfundedCross(FeatureBitset features)
void testGatewayCrossCurrency(FeatureBitset features)
void testOfferCreateThenCross(FeatureBitset features)
void testBookBaseDomainMptDistinct(FeatureBitset)
void testInsufficientReserve(FeatureBitset features)
void testOfferInScaling(FeatureBitset features)
void testRmSmallIncreasedQOffersXRP(FeatureBitset features)
void testSelfAuth(FeatureBitset features)
void testMPTIssuerOfferUsesRemainingCapacity(FeatureBitset features)
void testTransferRateOffer(FeatureBitset features)
void testMPTOfferZeroRateTickSizeCross(FeatureBitset features)
void testTransferRateOverflowOffer(FeatureBitset features)
void testCurrencyConversionIntoDebt(FeatureBitset features)
void testSelfCrossOffer2(FeatureBitset features)
void testBridgedSecondLegDry(FeatureBitset features)
void testMPTOfferZeroRatePartialCross(FeatureBitset features)
void testTicketOffer(FeatureBitset features)
static std::vector< SLE::const_pointer > sortedOffersOnAccount(jtx::Env &env, jtx::Account const &acct)
void testRmSmallIncreasedQOffersMPT(FeatureBitset features)
void testPartiallyFundedMPTInputOfferZeroInput(FeatureBitset features)
void testTinyPayment(FeatureBitset features)
void testSelfCross(bool usePartner, FeatureBitset features)
void testCrossCurrencyBridged(FeatureBitset features)
void testNegativeBalance(FeatureBitset features)
void testSelfCrossOffer1(FeatureBitset features)
void testSellFlagBasic(FeatureBitset features)
void testBookBaseMixedAssetCollision(FeatureBitset)
void testSelfCrossOffer(FeatureBitset features)
void testDirectCross(FeatureBitset features)
void testSelfCrossLowQualityOffer(FeatureBitset features)
void testRmFundedOffer(FeatureBitset features)
void testMPTOfferZeroRateCrossable(FeatureBitset features)
void testMissingAuth(FeatureBitset features)
void testPartialCross(FeatureBitset features)
void testDeletedOfferIssuer(FeatureBitset features)
void testOfferCrossWithXRP(bool reverseOrder, FeatureBitset features)
void testSellWithFillOrKill(FeatureBitset features)
void testSellOffer(FeatureBitset features)
void testMPTAMMLimitQualityRounding(FeatureBitset features)
void testTicketCancelOffer(FeatureBitset features)
void testBookOffersMPTFunding(FeatureBitset features)
void testDirectToDirectPath(FeatureBitset features)
void testFillOrKill(FeatureBitset features)
void testCrossCurrencyStartXRP(FeatureBitset features)
void testZeroRateXrpIouOffer(FeatureBitset features)
void testMPTOfferZeroRate(FeatureBitset features)
void testExpiration(FeatureBitset features)
void testFillModes(FeatureBitset features)
void testCrossCurrencyEndXRP(FeatureBitset features)
void testOfferCrossWithLimitOverride(FeatureBitset features)
void testXRPTinyPayment(FeatureBitset features)
void testTickSize(FeatureBitset features)
void testMPTOfferZeroRateFlags(FeatureBitset features)
void testAutoCreateReserve(FeatureBitset features)
void testOfferFeesConsumeFunds(FeatureBitset features)
void testAll(FeatureBitset features)
void testBridgedCross(FeatureBitset features)
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
void testSelfIssueOffer(FeatureBitset features)
void testBookBaseMptMptDistinct(FeatureBitset)
void testSelfPayXferFeeOffer(FeatureBitset features)
Convenience class to test AMM functionality.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
AccountID id() const
Returns the Account ID.
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.
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
PrettyAmount limit(Account const &account, Issue const &issue) const
Returns the IOU limit on an account.
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.
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
std::int64_t getBalance(Account const &account) const
void pay(Account const &src, Account const &dest, std::int64_t amount, std::optional< TER > err=std::nullopt, std::optional< std::vector< std::string > > credentials=std::nullopt, std::source_location const &loc=std::source_location::current())
void authorize(MPTAuthorize const &arg=MPTAuthorize{}, std::source_location const &loc=std::source_location::current())
Converts to MPT Issue or STAmount.
Match the number of items in the account's owner directory.
Check a set of conditions.
Sets the SendMax on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Set a ticket sequence on a JTx.
@ Array
array value (ordered list)
Keylet offer(AccountID const &id, SeqProxy const &seq) noexcept
An offer from an account.
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
json::Value ledgerEntryMPT(jtx::Env &env, jtx::Account const &acct, MPTID const &mptID)
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
bool expectOffers(Env &env, AccountID const &account, std::uint16_t size, std::vector< Amounts > const &toMatch)
json::Value offerCancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
void testHelper2TokensMix(TTester &&tester)
XrpT const XRP
Converts to XRP Issue or STAmount.
json::Value noop(Account const &account)
The null transaction.
json::Value getBookOffers(jtx::Env &env, Asset const &takerPays, Asset const &takerGets)
XRPAmount txFee(Env const &env, std::uint16_t n)
FeatureBitset testableAmendments()
void testHelper3TokensMix(TTester &&tester)
OwnerCount< ltOFFER > Offers
Match the number of offers in the account's owner directory.
json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
json::Value ledgerEntryRoot(Env &env, Account const &acct)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
OwnerCount< ltMPTOKEN > MPTokens
Match the number of MPToken in the account's owner directory.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
OwnerCount< ltTICKET > Tickets
Match the number of tickets on the account.
json::Value ledgerEntryOffer(jtx::Env &env, jtx::Account const &acct, std::uint32_t offerSeq)
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
constexpr XRPAmount
Convert XRP to drops (integral types).
BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, xrpl, 2)
bool isOffer(jtx::Env &env, jtx::Account const &account, STAmount const &takerPays, STAmount const &takerGets)
An offer exists.
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpcVersion, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(SLE::ConstRef)> const &f)
Iterate all items in the given directory.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
UInt256 getBookBase(Book const &book)
std::uint64_t getQuality(UInt256 const &uBase)
int scale(Number const &number, Asset const &asset)
Get the scale of a Number for a given asset.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
std::string to_string(BaseUInt< Bits, Tag > const &a)
@ Actor
Another account signed and submitted transactions on behalf of this account (this account is the owne...
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
json::Value getJson(LedgerFill const &fill)
Return a new json::Value representing the ledger with given options.
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
constexpr std::uint16_t kMaxTransferFee
The maximum token transfer fee allowed.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
Represents an XRP, IOU, or MPT quantity This customizes the string conversion and supports XRP conver...
STAmount const & value() const