2#include <test/jtx/AMM.h>
3#include <test/jtx/AMMTest.h>
4#include <test/jtx/PathSet.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/sendmax.h>
8#include <xrpld/app/misc/AMMUtils.h>
9#include <xrpld/app/paths/AMMContext.h>
10#include <xrpld/app/paths/AMMOffer.h>
11#include <xrpld/app/paths/Flow.h>
12#include <xrpld/app/paths/detail/StrandFlow.h>
14#include <xrpl/ledger/PaymentSandbox.h>
15#include <xrpl/protocol/Feature.h>
16#include <xrpl/protocol/STParsedJSON.h>
36 testcase(
"Incorrect Removal of Funded Offers");
49 Env env{*
this, features};
71 if (!features[fixAMMv1_1])
74 STAmount{BTC, UINT64_C(1'001'000000374812), -12},
USD(100'000), ammCarol.
tokens()));
79 STAmount{BTC, UINT64_C(1'001'000000374815), -12},
USD(100'000), ammCarol.
tokens()));
94 Env env{*
this, features};
99 auto const USD1 = gw1[
"USD"];
100 auto const USD2 = gw2[
"USD"];
110 env(
pay(gw1, dan, USD1(10'000)));
111 env(
pay(gw1,
bob, USD1(50)));
112 env(
pay(gw2,
bob, USD2(50)));
115 AMM ammDan(env, dan,
XRP(10'000), USD1(10'000));
126 Env env{*
this, features};
131 auto const USD1 = gw1[
"USD"];
132 auto const USD2 = gw2[
"USD"];
135 env.fund(
XRP(20'000), dan);
141 env(
pay(gw1, dan, USD1(10'050)));
142 env(
pay(gw1,
bob, USD1(50)));
143 env(
pay(gw2,
bob, USD2(50)));
146 AMM ammDan(env, dan,
XRP(10'000), USD1(10'050));
164 auto const startBalance =
XRP(1'000'000);
170 [&](
AMM& ammAlice,
Env& env) {
188 {{
XRP(10'100),
USD(10'000)}},
196 [&](
AMM& ammAlice,
Env& env) {
208 {{
XRP(10'100),
USD(10'000)}},
215 [&](
AMM& ammAlice,
Env& env) {
223 {{
XRP(10'100),
USD(10'000)}},
230 [&](
AMM& ammAlice,
Env& env) {
243 {{
XRP(11'000),
USD(9'000)}},
252 testcase(
"Offer Crossing with XRP, Normal order");
256 Env env{*
this, features};
265 auto const xrpTransferred =
XRPAmount{3'061'224'490};
268 BEAST_EXPECT(ammAlice.expectBalances(
XRP(150'000) + xrpTransferred,
USD(49),
IOUAmount{273'861'278752583, -8}));
278 testcase(
"Offer Crossing with Limit Override");
282 Env env{*
this, features};
297 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] ==
"-1");
300 jrr[jss::node][sfBalance.fieldName] ==
301 to_string((
XRP(200'000) -
XRP(3'000) - env.current()->fees().base * 1).xrp()));
307 testcase(
"Currency Conversion: Entire Offer");
311 Env env{*
this, features};
332 jrr[jss::node][sfBalance.fieldName] ==
333 to_string((
XRP(10'000) +
XRP(500) - env.current()->fees().base * 2).xrp()));
339 testcase(
"Currency Conversion: In Parts");
344 [&](
AMM& ammAlice,
Env& env) {
360 {{
XRP(10'000),
USD(10'000)}},
369 testcase(
"Cross Currency Payment: Start with XRP");
374 [&](
AMM& ammAlice,
Env& env) {
383 {{
XRP(10'000),
USD(10'100)}},
392 testcase(
"Cross Currency Payment: End with XRP");
397 [&](
AMM& ammAlice,
Env& env) {
406 {{
XRP(10'100),
USD(10'000)}},
415 testcase(
"Cross Currency Payment: Bridged");
419 Env env{*
this, features};
421 auto const gw1 =
Account{
"gateway_1"};
422 auto const gw2 =
Account{
"gateway_2"};
423 auto const dan =
Account{
"dan"};
424 auto const USD1 = gw1[
"USD"];
425 auto const EUR1 = gw2[
"EUR"];
434 env(
trust(dan, EUR1(1'000)));
440 env(
pay(gw2, dan, dan[
"EUR"](400)));
443 AMM ammCarol(env,
carol, USD1(5'000),
XRP(50'000));
445 env(
offer(dan,
XRP(500), EUR1(50)));
449 jtp[0u][0u][jss::currency] =
"XRP";
461 testcase(
"Offer Fees Consume Funds");
465 Env env{*
this, features};
467 auto const gw1 =
Account{
"gateway_1"};
468 auto const gw2 =
Account{
"gateway_2"};
469 auto const gw3 =
Account{
"gateway_3"};
472 auto const USD1 = gw1[
"USD"];
473 auto const USD2 = gw2[
"USD"];
474 auto const USD3 = gw3[
"USD"];
482 auto const starting_xrp =
XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4;
484 env.fund(starting_xrp, gw1, gw2, gw3,
alice);
485 env.fund(
XRP(2'000),
bob);
496 AMM ammBob(env,
bob,
XRP(1'000), USD1(1'200));
507 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] ==
"109.090909090909");
509 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName] ==
XRP(350).value().getText());
515 testcase(
"Offer Create, then Cross");
519 Env env{*
this, features};
538 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] ==
"-0.8995000001");
544 testcase(
"Offer tfSell: Basic Sell");
549 [&](
AMM& ammAlice,
Env& env) {
557 {{
XRP(9'900),
USD(10'100)}},
566 testcase(
"Offer tfSell: 2x Sell Exceed Limit");
570 Env env{*
this, features};
572 auto const starting_xrp =
XRP(100) +
reserve(env, 1) + env.current()->fees().base * 2;
574 env.fund(starting_xrp,
gw,
alice);
575 env.fund(
XRP(2'000),
bob);
599 testcase(
"Client Issue: Gateway Cross Currency");
603 Env env{*
this, features};
605 auto const XTS =
gw[
"XTS"];
606 auto const XXX =
gw[
"XXX"];
608 auto const starting_xrp =
XRP(100.1) +
reserve(env, 1) + env.current()->fees().base * 2;
609 fund(env,
gw, {
alice,
bob}, starting_xrp, {XTS(100), XXX(100)}, Fund::All);
611 AMM ammAlice(env,
alice, XTS(100), XXX(100));
615 payment[jss::id] = env.seq(
bob);
616 payment[jss::build_path] =
true;
618 payment[jss::tx_json][jss::Sequence] = env.current()->read(
keylet::account(
bob.
id()))->getFieldU32(sfSequence);
619 payment[jss::tx_json][jss::Fee] =
to_string(env.current()->fees().base);
622 auto const jrr = env.rpc(
"json",
"submit",
to_string(payment));
623 BEAST_EXPECT(jrr[jss::result][jss::status] ==
"success");
624 BEAST_EXPECT(jrr[jss::result][jss::engine_result] ==
"tesSUCCESS");
625 if (!features[fixAMMv1_1])
628 ammAlice.expectBalances(
STAmount(XTS, UINT64_C(101'010101010101), -12), XXX(99), ammAlice.tokens()));
634 ammAlice.expectBalances(
STAmount(XTS, UINT64_C(101'0101010101011), -13), XXX(99), ammAlice.tokens()));
648 Env env{*
this, features};
666 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100),
USD(10'000), ammAlice.tokens()));
674 Env env{*
this, features};
694 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100),
USD(10'000), ammAlice.tokens()));
702 Env env{*
this, features};
735 testcase(
"Combine tfSell with tfFillOrKill");
743 Env env{*
this, features};
749 BEAST_EXPECT(ammBob.expectBalances(
XRP(20'000),
USD(200), ammBob.tokens()));
753 Env env{*
this, features};
761 ammBob.expectBalances(
XRP(20'220),
STAmount{
USD, UINT64_C(197'8239366963403), -13}, ammBob.tokens()));
768 Env env{*
this, features};
776 ammBob.expectBalances(
XRP(21'500),
STAmount{
USD, UINT64_C(186'046511627907), -12}, ammBob.tokens()));
786 Env env{*
this, features};
806 [&](
AMM& ammAlice,
Env& env) {
818 {{
XRP(10'000),
USD(10'100)}},
826 [&](
AMM& ammAlice,
Env& env) {
838 {{
XRP(10'100),
USD(10'000)}},
845 Env env{*
this, features};
876 Env env{*
this, features};
911 Env env{*
this, features};
954 Env env{*
this, features};
1001 Env env{*
this, features};
1003 auto const USD_bob =
bob[
"USD"];
1004 auto const f = env.current()->fees().base;
1008 AMM ammBob(env,
bob,
XRP(10'000), USD_bob(10'100));
1026 using namespace jtx;
1028 Env env{*
this, features};
1031 auto const fee = env.current()->fees().base;
1034 auto const ann =
Account(
"ann");
1035 auto const A_BUX = ann[
"BUX"];
1037 auto const cam =
Account(
"cam");
1038 auto const dan =
Account(
"dan");
1039 auto const D_BUX = dan[
"BUX"];
1047 env(
trust(cam, D_BUX(100)));
1049 env(
pay(dan,
bob, D_BUX(100)));
1065 AMM ammBob(env,
bob, A_BUX(30), D_BUX(30));
1067 env(
trust(ann, D_BUX(100)));
1093 using namespace jtx;
1095 Env env{*
this, features};
1097 auto const ann =
Account(
"ann");
1099 auto const cam =
Account(
"cam");
1101 auto const A_BUX = ann[
"BUX"];
1102 auto const B_BUX =
bob[
"BUX"];
1104 auto const fee = env.current()->fees().base;
1109 env(
trust(ann, B_BUX(40)));
1110 env(
trust(cam, A_BUX(40)));
1112 env(
trust(cam, B_BUX(40)));
1117 env(
pay(ann, cam, A_BUX(35)));
1118 env(
pay(
bob, cam, B_BUX(35)));
1122 AMM ammCarol(env,
carol, A_BUX(300), B_BUX(330));
1129 env.require(
balance(cam, A_BUX(35)));
1130 env.require(
balance(cam, B_BUX(35)));
1131 env.require(
offers(cam, 1));
1134 env(
offer(cam, B_BUX(30), A_BUX(30)));
1137 if (!features[fixAMMv1_1])
1140 STAmount{A_BUX, UINT64_C(309'3541659651605), -13},
1141 STAmount{B_BUX, UINT64_C(320'0215509984417), -13},
1148 STAmount{B_BUX, UINT64_C(20'0215509984417), -13},
1149 STAmount{A_BUX, UINT64_C(20'0215509984417), -13}}}}));
1154 STAmount{A_BUX, UINT64_C(309'3541659651604), -13},
1155 STAmount{B_BUX, UINT64_C(320'0215509984419), -13},
1162 STAmount{B_BUX, UINT64_C(20'0215509984419), -13},
1163 STAmount{A_BUX, UINT64_C(20'0215509984419), -13}}}}));
1172 using namespace jtx;
1174 Env env{*
this, features};
1176 auto const aliceUSD =
alice[
"USD"];
1177 auto const bobUSD =
bob[
"USD"];
1219 using namespace jtx;
1221 Env env{*
this, features};
1282 using namespace jtx;
1319 using namespace jtx;
1331 BEAST_EXPECT(st.
empty());
1334 BEAST_EXPECT(sa ==
XRP(100'000'000));
1337 BEAST_EXPECT(
equal(da,
STAmount{
bob[
"USD"].issue(), UINT64_C(99'9999000001), -10}));
1347 using namespace jtx;
1350 auto const AUD =
gw[
"AUD"];
1372 using namespace jtx;
1373 auto const charlie =
Account(
"charlie");
1378 AMM ammCharlie(env, charlie,
XRP(10),
USD(11));
1380 BEAST_EXPECT(sa ==
XRP(1));
1382 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1384 auto const& pathElem = st[0][0];
1386 pathElem.isOffer() && pathElem.getIssuerID() ==
gw.
id() && pathElem.getCurrency() ==
USD.
currency);
1393 AMM ammCharlie(env, charlie,
XRP(11),
USD(10));
1396 BEAST_EXPECT(sa ==
USD(1));
1398 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1400 auto const& pathElem = st[0][0];
1402 pathElem.isOffer() && pathElem.getIssuerID() ==
xrpAccount() &&
1411 testcase(
"Path Find: XRP -> XRP and XRP -> IOU");
1412 using namespace jtx;
1424 env.
fund(
XRP(1'000), A3, G1, G2, G3);
1428 env.
trust(G1[
"XYZ"](5'000), A1);
1429 env.
trust(G3[
"ABC"](5'000), A1);
1430 env.
trust(G2[
"XYZ"](5'000), A2);
1431 env.
trust(G3[
"ABC"](5'000), A2);
1432 env.
trust(A2[
"ABC"](1'000), A3);
1433 env.
trust(G1[
"XYZ"](100'000), M1);
1434 env.
trust(G2[
"XYZ"](100'000), M1);
1435 env.
trust(G3[
"ABC"](100'000), M1);
1438 env(
pay(G1, A1, G1[
"XYZ"](3'500)));
1439 env(
pay(G3, A1, G3[
"ABC"](1'200)));
1440 env(
pay(G1, M1, G1[
"XYZ"](25'000)));
1441 env(
pay(G2, M1, G2[
"XYZ"](25'000)));
1442 env(
pay(G3, M1, G3[
"ABC"](25'000)));
1445 AMM ammM1_G1_G2(env, M1, G1[
"XYZ"](1'000), G2[
"XYZ"](1'000));
1446 AMM ammM1_XRP_G3(env, M1,
XRP(10'000), G3[
"ABC"](1'000));
1452 auto const& send_amt =
XRP(10);
1454 BEAST_EXPECT(
equal(da, send_amt));
1455 BEAST_EXPECT(st.
empty());
1461 auto const& send_amt =
XRP(200);
1463 BEAST_EXPECT(
equal(da, send_amt));
1464 BEAST_EXPECT(st.
empty());
1468 auto const& send_amt = G3[
"ABC"](10);
1470 BEAST_EXPECT(
equal(da, send_amt));
1476 auto const& send_amt = A2[
"ABC"](1);
1478 BEAST_EXPECT(
equal(da, send_amt));
1484 auto const& send_amt = A3[
"ABC"](1);
1486 BEAST_EXPECT(
equal(da, send_amt));
1495 testcase(
"Path Find: non-XRP -> XRP");
1496 using namespace jtx;
1503 env.
fund(
XRP(1'000), A1, A2, G3);
1507 env.
trust(G3[
"ABC"](1'000), A1, A2);
1508 env.
trust(G3[
"ABC"](100'000), M1);
1511 env(
pay(G3, A1, G3[
"ABC"](1'000)));
1512 env(
pay(G3, A2, G3[
"ABC"](1'000)));
1513 env(
pay(G3, M1, G3[
"ABC"](1'200)));
1516 AMM ammM1(env, M1, G3[
"ABC"](1'000),
XRP(10'010));
1521 auto const& send_amt =
XRP(10);
1523 BEAST_EXPECT(
equal(da, send_amt));
1524 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1531 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1532 using namespace jtx;
1545 env.
fund(
XRP(1'000), A1, A2, A3, G1, G2, G3, G4);
1547 env.
fund(
XRP(21'000), M1, M2);
1550 env.
trust(G1[
"HKD"](2'000), A1);
1551 env.
trust(G2[
"HKD"](2'000), A2);
1552 env.
trust(G1[
"HKD"](2'000), A3);
1553 env.
trust(G1[
"HKD"](100'000), M1);
1554 env.
trust(G2[
"HKD"](100'000), M1);
1555 env.
trust(G1[
"HKD"](100'000), M2);
1556 env.
trust(G2[
"HKD"](100'000), M2);
1559 env(
pay(G1, A1, G1[
"HKD"](1'000)));
1560 env(
pay(G2, A2, G2[
"HKD"](1'000)));
1561 env(
pay(G1, A3, G1[
"HKD"](1'000)));
1562 env(
pay(G1, M1, G1[
"HKD"](1'200)));
1563 env(
pay(G2, M1, G2[
"HKD"](5'000)));
1564 env(
pay(G1, M2, G1[
"HKD"](1'200)));
1565 env(
pay(G2, M2, G2[
"HKD"](5'000)));
1568 AMM ammM1(env, M1, G1[
"HKD"](1'010), G2[
"HKD"](1'000));
1569 AMM ammM2XRP_G2(env, M2,
XRP(10'000), G2[
"HKD"](1'010));
1570 AMM ammM2G1_XRP(env, M2, G1[
"HKD"](1'010),
XRP(10'000));
1578 auto const& send_amt = G1[
"HKD"](10);
1580 BEAST_EXPECT(st.
empty());
1581 BEAST_EXPECT(
equal(da, send_amt));
1582 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1588 auto const& send_amt = A1[
"HKD"](10);
1590 BEAST_EXPECT(st.
empty());
1591 BEAST_EXPECT(
equal(da, send_amt));
1592 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1598 auto const& send_amt = A3[
"HKD"](10);
1600 BEAST_EXPECT(
equal(da, send_amt));
1601 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1608 auto const& send_amt = G2[
"HKD"](10);
1610 BEAST_EXPECT(
equal(da, send_amt));
1611 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1619 auto const& send_amt = G2[
"HKD"](10);
1621 BEAST_EXPECT(
equal(da, send_amt));
1622 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1635 auto const& send_amt = A2[
"HKD"](10);
1637 BEAST_EXPECT(
equal(da, send_amt));
1638 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1651 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1652 using namespace jtx;
1662 env.
fund(
XRP(1'000), A1, A2, A3, G1, G2);
1665 env.
trust(G1[
"HKD"](2'000), A1);
1666 env.
trust(G2[
"HKD"](2'000), A2);
1667 env.
trust(A2[
"HKD"](2'000), A3);
1668 env.
trust(G1[
"HKD"](100'000), M1);
1669 env.
trust(G2[
"HKD"](100'000), M1);
1672 env(
pay(G1, A1, G1[
"HKD"](1'000)));
1673 env(
pay(G2, A2, G2[
"HKD"](1'000)));
1674 env(
pay(G1, M1, G1[
"HKD"](5'000)));
1675 env(
pay(G2, M1, G2[
"HKD"](5'000)));
1678 AMM ammM1(env, M1, G1[
"HKD"](1'010), G2[
"HKD"](1'000));
1682 auto const& send_amt = A2[
"HKD"](10);
1686 BEAST_EXPECT(
equal(da, send_amt));
1687 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1696 using namespace jtx;
1698 Env env(*
this, features);
1704 auto const AMMXRPPool = env.
current()->fees().increment * 2;
1721 AMM ammBob(env,
bob, AMMXRPPool,
USD(150));
1729 BEAST_EXPECT(carolUSD >
USD(0) && carolUSD <
USD(50));
1737 using namespace jtx;
1741 Env env(*
this, features);
1753 BEAST_EXPECT(ammBob.expectBalances(
BTC(150),
USD(100), ammBob.tokens()));
1757 Env env(*
this, features);
1770 BEAST_EXPECT(ammBobBTC_XRP.expectBalances(
BTC(150),
XRP(100), ammBobBTC_XRP.tokens()));
1775 Env env(*
this, features);
1787 BEAST_EXPECT(ammBob.expectBalances(
XRP(150),
USD(100), ammBob.tokens()));
1791 Env env(*
this, features);
1803 BEAST_EXPECT(ammBob.expectBalances(
USD(150),
XRP(100), ammBob.tokens()));
1807 Env env(*
this, features);
1856 Env env(*
this, features);
1889 auto const flowResult = [&] {
1904 paths.push_back(p1);
1907 paths.push_back(p2);
1926 BEAST_EXPECT(flowResult.removableOffers.size() == 1);
1928 if (flowResult.removableOffers.empty())
1931 for (
auto const& o : flowResult.removableOffers)
1953 Env env(*
this, features);
1976 BEAST_EXPECT(ammBob.expectBalances(
USD(8.4),
XRPAmount{20}, ammBob.tokens()));
1984 using namespace jtx;
1988 Env env(*
this, features);
2005 if (!features[fixAMMv1_1])
2009 amm.expectBalances(
GBP(1'120),
STAmount{
USD, UINT64_C(892'8571428571428), -13}, amm.tokens()));
2014 amm.expectBalances(
GBP(1'120),
STAmount{
USD, UINT64_C(892'8571428571429), -13}, amm.tokens()));
2023 Env env(*
this, features);
2051 BEAST_EXPECT(amm.expectBalances(
EUR(1'096),
STAmount{
USD, UINT64_C(912'4087591240876), -13}, amm.tokens()));
2057 Env env(*
this, features);
2065 AMM amm2(env, ed,
EUR(1'000),
USD(1'000));
2074 if (!features[fixAMMv1_1])
2107 Env env(*
this, features);
2118 BEAST_EXPECT(amm.expectBalances(
USD(1'100),
EUR(1'000), amm.tokens()));
2126 Env env(*
this, features);
2147 BEAST_EXPECT(amm.expectBalances(
STAmount{GBP, UINT64_C(1'142'857142857143), -12},
USD(875), amm.tokens()));
2155 Env env(*
this, features);
2171 if (!features[fixAMMv1_1])
2178 BEAST_EXPECT(amm.expectBalances(
GBP(1'024),
USD(1'171.875), amm.tokens()));
2188 amm.expectBalances(
STAmount{GBP, UINT64_C(1'024'000000000001), -12},
USD(1'171.875), amm.tokens()));
2197 Env env(*
this, features);
2217 if (!features[fixAMMv1_1])
2229 STAmount{
GBP, UINT64_C(1'470'421052631579), -12}));
2236 STAmount{
EUR, UINT64_C(929'5789473684212), -13}}}));
2239 BEAST_EXPECT(amm.expectBalances(
2240 STAmount{EUR, UINT64_C(1'056'336842105263), -12},
2241 STAmount{USD, UINT64_C(1'325'334821428571), -12},
2256 STAmount{
GBP, UINT64_C(1'470'42105263158), -11}));
2263 STAmount{
EUR, UINT64_C(929'57894736842), -11}}}));
2266 BEAST_EXPECT(amm.expectBalances(
2267 STAmount{EUR, UINT64_C(1'056'336842105264), -12},
2268 STAmount{USD, UINT64_C(1'325'334821428571), -12},
2277 Env env(*
this, features);
2297 if (!features[fixAMMv1_1])
2306 BEAST_EXPECT(amm.expectBalances(
2307 STAmount{GBP, UINT64_C(1'056'336842105263), -12},
2308 STAmount{EUR, UINT64_C(946'6677295918366), -13},
2320 BEAST_EXPECT(amm.expectBalances(
2321 STAmount{GBP, UINT64_C(1'056'336842105264), -12},
2322 STAmount{EUR, UINT64_C(946'6677295918366), -13},
2331 STAmount{
EUR, UINT64_C(1'442'665816326531), -12}));
2338 STAmount{
USD, UINT64_C(1'340'267857142857), -12}}}));
2345 Env env(*
this, features);
2353 AMM amm2(env, ed,
EUR(1'000),
USD(1'400));
2363 if (!features[fixAMMv1_1])
2371 STAmount{GBP, UINT64_C(1'086'024691358025), -12},
2372 STAmount{EUR, UINT64_C(920'78937795562), -11},
2377 STAmount{EUR, UINT64_C(1'063'368497635504), -12},
2378 STAmount{USD, UINT64_C(1'316'570881226053), -12},
2389 STAmount{GBP, UINT64_C(1'086'024691358027), -12},
2390 STAmount{EUR, UINT64_C(920'7893779556188), -13},
2395 STAmount{EUR, UINT64_C(1'063'368497635505), -12},
2396 STAmount{USD, UINT64_C(1'316'570881226053), -12},
2405 Env env(*
this, features);
2422 if (!features[fixAMMv1_1])
2426 STAmount{GBP, UINT64_C(1'108'148148148149), -12},
2427 STAmount{EUR, UINT64_C(902'4064171122988), -13},
2432 STAmount{EUR, UINT64_C(1'078'074866310161), -12},
2433 STAmount{USD, UINT64_C(1'298'611111111111), -12},
2440 STAmount{GBP, UINT64_C(1'108'148148148151), -12},
2441 STAmount{EUR, UINT64_C(902'4064171122975), -13},
2446 STAmount{EUR, UINT64_C(1'078'074866310162), -12},
2447 STAmount{USD, UINT64_C(1'298'611111111111), -12},
2463 using namespace jtx;
2478 BEAST_EXPECT(ammBob.expectBalances(
XRP(1'050),
USD(1'000), ammBob.tokens()));
2489 using namespace jtx;
2527 auto const JPY =
gw[
"JPY"];
2535 AMM ammAliceXRP_JPY(env,
alice,
XRP(100), JPY(100));
2550 using namespace jtx;
2551 Env env(*
this, features);
2552 auto const dan =
Account(
"dan");
2553 auto const ed =
Account(
"ed");
2569 if (!features[fixAMMv1_1])
2593 testcase(
"Convert all of an asset using DeliverMin");
2595 using namespace jtx;
2598 Env env(*
this, features);
2622 Env env(*
this, features);
2636 Env env(*
this, features);
2657 auto const dan =
Account(
"dan");
2658 Env env(*
this, features);
2666 AMM ammDan(env, dan,
XRP(1'000),
USD(1'100));
2667 if (!features[fixAMMv1_1])
2698 using namespace jtx;
2704 Env env(*
this, features);
2740 using namespace jtx;
2761 auto failedIouPayments = [
this, &env]() {
2783 failedIouPayments();
2800 failedIouPayments();
2807 failedIouPayments();
2829 using namespace test::jtx;
2830 Env env(*
this, features);
2843 env(
pay(G1,
bob, G1[
"USD"](10)));
2844 env(
pay(G1,
alice, G1[
"USD"](205)));
2847 AMM ammAlice(env,
alice,
XRP(500), G1[
"USD"](105));
2853 BEAST_EXPECT(
lines[jss::lines][0u][jss::account] == G1.human());
2854 BEAST_EXPECT(
lines[jss::lines][0u][jss::limit] ==
"100");
2855 BEAST_EXPECT(
lines[jss::lines][0u][jss::balance] ==
"10");
2862 BEAST_EXPECT(
lines[jss::lines][0u][jss::account] == G1.human());
2863 BEAST_EXPECT(
lines[jss::lines][0u][jss::limit] ==
"205");
2865 BEAST_EXPECT(
lines[jss::lines][0u][jss::balance] ==
"100");
2905 for (
auto const& it :
lines[jss::lines])
2907 if (it[jss::account] ==
bob.
human())
2913 if (!BEAST_EXPECT(bobLine))
2915 BEAST_EXPECT(bobLine[jss::freeze] ==
true);
2916 BEAST_EXPECT(bobLine[jss::balance] ==
"-16");
2923 for (
auto const& it :
lines[jss::lines])
2925 if (it[jss::account] == G1.human())
2931 if (!BEAST_EXPECT(g1Line))
2933 BEAST_EXPECT(g1Line[jss::freeze_peer] ==
true);
2934 BEAST_EXPECT(g1Line[jss::balance] ==
"16");
2944 auto ff = affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
2945 BEAST_EXPECT(ff[sfLowLimit.fieldName] == G1[
"USD"](0).value().getJson(
JsonOptions::none));
2946 BEAST_EXPECT(!(ff[jss::Flags].asUInt() &
lsfLowFreeze));
2957 using namespace test::jtx;
2958 Env env(*
this, features);
2968 env.
fund(
XRP(20'000), A2, A3, A4);
2971 env.
trust(G1[
"USD"](1'200), A1);
2972 env.
trust(G1[
"USD"](200), A2);
2973 env.
trust(G1[
"BTC"](100), A3);
2974 env.
trust(G1[
"BTC"](100), A4);
2977 env(
pay(G1, A1, G1[
"USD"](1'000)));
2978 env(
pay(G1, A2, G1[
"USD"](100)));
2979 env(
pay(G1, A3, G1[
"BTC"](100)));
2980 env(
pay(G1, A4, G1[
"BTC"](100)));
2983 AMM ammG1(env, G1,
XRP(10'000), G1[
"USD"](100));
2992 auto offers = env.
rpc(
"book_offers",
std::string(
"USD/") + G1.human(),
"XRP")[jss::result][jss::offers];
3000 BEAST_EXPECT(accounts.
find(A2.human()) !=
std::end(accounts));
3003 offers = env.
rpc(
"book_offers",
"XRP",
std::string(
"USD/") + G1.human())[jss::result][jss::offers];
3011 BEAST_EXPECT(accounts.
find(A1.human()) !=
std::end(accounts));
3018 AMM ammA3(env, A3, G1[
"BTC"](1),
XRP(1));
3024 env(
pay(G1, A2, G1[
"USD"](1)));
3027 env(
pay(A2, G1, G1[
"USD"](1)));
3030 env(
pay(A2, A1, G1[
"USD"](1)));
3033 env(
pay(A1, A2, G1[
"USD"](1)));
3056 auto offers = env.
rpc(
"book_offers",
"XRP",
std::string(
"USD/") + G1.human())[jss::result][jss::offers];
3060 offers = env.
rpc(
"book_offers",
std::string(
"USD/") + G1.human(),
"XRP")[jss::result][jss::offers];
3068 env(
pay(G1, A2, G1[
"USD"](1)));
3071 env(
pay(A2, G1, G1[
"USD"](1)));
3081 testcase(
"Offers for Frozen Trust Lines");
3083 using namespace test::jtx;
3084 Env env(*
this, features);
3091 env.
fund(
XRP(2'000), G1, A3, A4);
3095 env.
trust(G1[
"USD"](1'000), A2);
3096 env.
trust(G1[
"USD"](2'000), A3);
3097 env.
trust(G1[
"USD"](2'001), A4);
3100 env(
pay(G1, A3, G1[
"USD"](2'000)));
3101 env(
pay(G1, A4, G1[
"USD"](2'001)));
3104 AMM ammA3(env, A3,
XRP(1'000), G1[
"USD"](1'001));
3114 env(
offer(A4,
XRP(999), G1[
"USD"](999)));
3123 BEAST_EXPECT(info[jss::amm][jss::asset2_frozen].asBool());
3138 env(
offer(A2, G1[
"USD"](999),
XRP(999)));
3150 testcase(
"Multisign AMM Transactions");
3152 using namespace jtx;
3153 Env env{*
this, features};
3170 msig const ms{becky, bogie};
3193 ammAlice.
vote({}, 1'000);
3196 env(ammAlice.
bid({.account = alice, .bidMin = 100}), ms).close();
3207 using namespace jtx;
3211 Env env(*
this, features);
3229 using namespace jtx;
3233 Env env(*
this, features);
3234 auto const BobUSD =
bob[
"USD"];
3235 auto const BobEUR =
bob[
"EUR"];
3240 fund(env,
bob, {
alice,
gw}, {BobUSD(100), BobEUR(100)}, Fund::IOUOnly);
3243 AMM ammBobXRP_USD(env,
bob,
XRP(100), BobUSD(100));
3246 AMM ammBobUSD_EUR(env,
bob, BobUSD(100), BobEUR(100));
3249 Path const p = [&] {
3266 Env env(*
this, features);
3277 Env env(*
this, features);
3296 using namespace jtx;
3298 auto const CNY =
gw[
"CNY"];
3301 Env env(*
this, features);
3322 Env env(*
this, features);
3336 AMM ammBobEUR_CNY(env,
bob,
EUR(100), CNY(100));
3362 using namespace jtx;
3378 using namespace jtx;
3389 using namespace jtx;
3410 using namespace test::jtx;
3450BEAST_DEFINE_TESTSUITE_PRIO(AMMExtended, app,
xrpl, 1);
A generic endpoint for log messages.
testcase_t testcase
Memberspace for declaring test cases.
virtual OpenLedger & openLedger()=0
Floating point representation of amounts with high dynamic range.
A currency issued by an account.
beast::Journal journal(std::string const &name)
Sets the new scale and restores the old scale when it leaves scope.
bool modify(modify_type const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
A wrapper which makes credits unavailable to balances.
Discardable, editable view to a ledger.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Path & push_back(Issue const &iss)
static FeatureBitset testable_amendments()
void testAMM(std::function< void(jtx::AMM &, jtx::Env &)> &&cb, std::optional< std::pair< STAmount, STAmount > > const &pool=std::nullopt, std::uint16_t tfee=0, std::optional< jtx::ter > const &ter=std::nullopt, std::vector< FeatureBitset > const &features={testable_amendments()})
testAMM() funds 30,000XRP and 30,000IOU for each non-XRP asset to Alice and Carol
XRPAmount ammCrtFee(jtx::Env &env) const
XRPAmount reserve(jtx::Env &env, std::uint32_t count) const
std::tuple< STPathSet, STAmount, STAmount > find_paths(jtx::Env &env, jtx::Account const &src, jtx::Account const &dst, STAmount const &saDstAmount, std::optional< STAmount > const &saSendMax=std::nullopt, std::optional< Currency > const &saSrcCurrency=std::nullopt)
Convenience class to test AMM functionality.
bool expectTradingFee(std::uint16_t fee) const
void vote(std::optional< Account > const &account, std::uint32_t feeVal, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< jtx::seq > const &seq=std::nullopt, std::optional< std::pair< Issue, Issue > > const &assets=std::nullopt, std::optional< ter > const &ter=std::nullopt)
bool expectAuctionSlot(std::uint32_t fee, std::optional< std::uint8_t > timeSlot, IOUAmount expectedPrice) const
IOUAmount withdrawAll(std::optional< Account > const &account, std::optional< STAmount > const &asset1OutDetails=std::nullopt, std::optional< ter > const &ter=std::nullopt)
IOUAmount withdraw(std::optional< Account > const &account, std::optional< LPToken > const &tokens, std::optional< STAmount > const &asset1OutDetails=std::nullopt, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< ter > const &ter=std::nullopt)
IOUAmount deposit(std::optional< Account > const &account, LPToken tokens, std::optional< STAmount > const &asset1InDetails=std::nullopt, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< ter > const &ter=std::nullopt)
AccountID const & ammAccount() const
Json::Value ammRpcInfo(std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< Issue > issue1=std::nullopt, std::optional< Issue > issue2=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt, bool ignoreParams=false, unsigned apiVersion=RPC::apiInvalidVersion) const
Send amm_info RPC command.
Json::Value bid(BidArg const &arg)
bool expectBalances(STAmount const &asset1, STAmount const &asset2, IOUAmount const &lpt, std::optional< AccountID > const &account=std::nullopt) const
Verify the AMM balances.
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.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &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.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
std::shared_ptr< STObject const > meta()
Return metadata for the last JTx.
void require(Args const &... args)
Check a set of requirements.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Sets the DeliverMin on a JTx.
Set a multisignature on a JTx.
Match clear account flags.
Match the number of items in the account's owner directory.
Set Paths, SendMax on a JTx.
Sets the QualityIn on a trust JTx.
Sets the QualityOut on a trust JTx as a percentage.
Check a set of conditions.
Sets the SendMax on a JTx.
Set the regular signature on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
@ arrayValue
array value (ordered list)
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Keylet account(AccountID const &id) noexcept
AccountID root.
bool expectLedgerEntryRoot(Env &env, Account const &acct, STAmount const &expectedValue)
Json::Value getAccountOffers(Env &env, AccountID const &acct, bool current)
PrettyAmount xrpMinusFee(Env const &env, std::int64_t xrpAmount)
bool expectOffers(Env &env, AccountID const &account, std::uint16_t size, std::vector< Amounts > const &toMatch)
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
bool expectHolding(Env &env, AccountID const &account, STAmount const &value, bool defaultLimits)
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Json::Value ledgerEntryState(Env &env, Account const &acct_a, Account const &acct_b, std::string const ¤cy)
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
bool same(STPathSet const &st1, Args const &... args)
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
XRPAmount txfee(Env const &env, std::uint16_t n)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
STPathElement allpe(AccountID const &a, Issue const &iss)
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
STPathElement IPE(Issue const &iss)
STPathElement cpe(Currency const &c)
Json::Value ledgerEntryRoot(Env &env, Account const &acct)
FeatureBitset testable_amendments()
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
owner_count< ltOFFER > offers
Match the number of offers in the account's owner directory.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
bool checkArraySize(Json::Value const &val, unsigned int size)
STPath stpath(Args const &... args)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
void n_offers(Env &env, std::size_t n, Account const &account, STAmount const &in, STAmount const &out)
Json::Value getAccountLines(Env &env, AccountID const &acctId)
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
bool equal(std::unique_ptr< Step > const &s1, DirectStepInfo const &dsi)
bool isOffer(jtx::Env &env, jtx::Account const &account, STAmount const &takerPays, STAmount const &takerGets)
An offer exists.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t tfSell
constexpr std::uint32_t asfGlobalFreeze
constexpr std::uint32_t tfPassive
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
constexpr std::uint32_t tfImmediateOrCancel
constexpr std::uint32_t asfDisableMaster
constexpr std::uint32_t tfFillOrKill
std::string to_string(base_uint< Bits, Tag > const &a)
constexpr std::uint32_t tfSetNoRipple
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
constexpr std::uint32_t tfLimitQuality
constexpr std::uint32_t asfDepositAuth
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Currency const & xrpCurrency()
XRP currency.
constexpr std::uint32_t tfClearFreeze
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
constexpr std::uint32_t tfNoRippleDirect
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
constexpr std::uint32_t tfSetfAuth
constexpr std::uint32_t asfRequireAuth
constexpr std::uint32_t asfNoFreeze
AccountID const & xrpAccount()
Compute AccountID from public key.
constexpr std::uint32_t tfPartialPayment
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
constexpr std::uint32_t tfSetFreeze
Tests of AMM that use offers too.
void testRippleState(FeatureBitset features)
void testOfferFeesConsumeFunds(FeatureBitset features)
void testTransferRateOffer(FeatureBitset features)
void testCrossCurrencyBridged(FeatureBitset features)
void testOffersWhenFrozen(FeatureBitset features)
void testDirectToDirectPath(FeatureBitset features)
void testTransferRateNoOwnerFee(FeatureBitset features)
void testFillModes(FeatureBitset features)
void testSellFlagExceedLimit(FeatureBitset features)
void testRmFundedOffer(FeatureBitset features)
void testSelfIssueOffer(FeatureBitset features)
void test_convert_all_of_an_asset(FeatureBitset features)
void testStepLimit(FeatureBitset features)
void testSellFlagBasic(FeatureBitset features)
void testTxMultisign(FeatureBitset features)
void via_offers_via_gateway()
void testEnforceNoRipple(FeatureBitset features)
void testGlobalFreeze(FeatureBitset features)
void testLoop(FeatureBitset features)
void testBadPathAssert(FeatureBitset features)
void run() override
Runs the suite.
void testBridgedCross(FeatureBitset features)
void testCrossingLimits()
void testMissingAuth(FeatureBitset features)
void path_find_consume_all()
void testCurrencyConversionEntire(FeatureBitset features)
void testCurrencyConversionInParts(FeatureBitset features)
void testToStrand(FeatureBitset features)
NumberMantissaScaleGuard const sg_
void testCrossCurrencyEndXRP(FeatureBitset features)
void testFalseDry(FeatureBitset features)
void testOfferCreateThenCross(FeatureBitset features)
void testOfferCrossWithXRP(FeatureBitset features)
void testBookStep(FeatureBitset features)
void testGatewayCrossCurrency(FeatureBitset features)
void testSellWithFillOrKill(FeatureBitset features)
void testRequireAuth(FeatureBitset features)
void testPayment(FeatureBitset features)
void testRIPD1373(FeatureBitset features)
void testOfferCrossWithLimitOverride(FeatureBitset features)
void testCrossCurrencyStartXRP(FeatureBitset features)
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
STAmount const & value() const