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};
56 {
USD(200'000),
BTC(2'000)});
79 if (!features[fixAMMv1_1])
82 STAmount{BTC, UINT64_C(1'001'000000374812), -12},
89 STAmount{BTC, UINT64_C(1'001'000000374815), -12},
106 Env env{*
this, features};
111 auto const USD1 = gw1[
"USD"];
112 auto const USD2 = gw2[
"USD"];
122 env(
pay(gw1, dan, USD1(10'000)));
123 env(
pay(gw1,
bob, USD1(50)));
124 env(
pay(gw2,
bob, USD2(50)));
127 AMM ammDan(env, dan,
XRP(10'000), USD1(10'000));
138 Env env{*
this, features};
143 auto const USD1 = gw1[
"USD"];
144 auto const USD2 = gw2[
"USD"];
147 env.fund(
XRP(20'000), dan);
153 env(
pay(gw1, dan, USD1(10'050)));
154 env(
pay(gw1,
bob, USD1(50)));
155 env(
pay(gw2,
bob, USD2(50)));
158 AMM ammDan(env, dan,
XRP(10'000), USD1(10'050));
165 XRP(10'050), USD1(10'000), ammDan.
tokens()));
181 auto const startBalance =
XRP(1'000'000);
187 [&](
AMM& ammAlice,
Env& env) {
213 {{
XRP(10'100),
USD(10'000)}},
221 [&](
AMM& ammAlice,
Env& env) {
237 {{
XRP(10'100),
USD(10'000)}},
244 [&](
AMM& ammAlice,
Env& env) {
254 {{
XRP(10'100),
USD(10'000)}},
261 [&](
AMM& ammAlice,
Env& env) {
276 {{
XRP(11'000),
USD(9'000)}},
285 testcase(
"Offer Crossing with XRP, Normal order");
289 Env env{*
this, features};
298 auto const xrpTransferred =
XRPAmount{3'061'224'490};
301 BEAST_EXPECT(ammAlice.expectBalances(
302 XRP(150'000) + xrpTransferred,
308 env,
bob,
XRP(300'000) - xrpTransferred -
txfee(env, 1)));
315 testcase(
"Offer Crossing with Limit Override");
319 Env env{*
this, features};
335 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] ==
"-1");
338 jrr[jss::node][sfBalance.fieldName] ==
340 (
XRP(200'000) -
XRP(3'000) - env.current()->fees().base * 1)
347 testcase(
"Currency Conversion: Entire Offer");
351 Env env{*
this, features};
373 jrr[jss::node][sfBalance.fieldName] ==
381 testcase(
"Currency Conversion: In Parts");
386 [&](
AMM& ammAlice,
Env& env) {
410 {{
XRP(10'000),
USD(10'000)}},
419 testcase(
"Cross Currency Payment: Start with XRP");
424 [&](
AMM& ammAlice,
Env& env) {
434 {{
XRP(10'000),
USD(10'100)}},
443 testcase(
"Cross Currency Payment: End with XRP");
448 [&](
AMM& ammAlice,
Env& env) {
459 {{
XRP(10'100),
USD(10'000)}},
468 testcase(
"Cross Currency Payment: Bridged");
472 Env env{*
this, features};
474 auto const gw1 =
Account{
"gateway_1"};
475 auto const gw2 =
Account{
"gateway_2"};
476 auto const dan =
Account{
"dan"};
477 auto const USD1 = gw1[
"USD"];
478 auto const EUR1 = gw2[
"EUR"];
487 env(
trust(dan, EUR1(1'000)));
493 env(
pay(gw2, dan, dan[
"EUR"](400)));
496 AMM ammCarol(env,
carol, USD1(5'000),
XRP(50'000));
498 env(
offer(dan,
XRP(500), EUR1(50)));
502 jtp[0u][0u][jss::currency] =
"XRP";
504 json(jss::Paths, jtp),
509 STAmount{USD1, UINT64_C(5'030'181086519115), -12},
518 testcase(
"Offer Fees Consume Funds");
522 Env env{*
this, features};
524 auto const gw1 =
Account{
"gateway_1"};
525 auto const gw2 =
Account{
"gateway_2"};
526 auto const gw3 =
Account{
"gateway_3"};
529 auto const USD1 = gw1[
"USD"];
530 auto const USD2 = gw2[
"USD"];
531 auto const USD3 = gw3[
"USD"];
539 auto const starting_xrp =
XRP(100) +
540 env.current()->fees().accountReserve(3) +
541 env.current()->fees().base * 4;
543 env.fund(starting_xrp, gw1, gw2, gw3,
alice);
544 env.fund(
XRP(2'000),
bob);
555 AMM ammBob(env,
bob,
XRP(1'000), USD1(1'200));
564 STAmount{USD1, UINT64_C(1'090'909090909091), -12},
569 jrr[jss::node][sfBalance.fieldName][jss::value] ==
573 jrr[jss::node][sfBalance.fieldName] ==
XRP(350).value().getText());
579 testcase(
"Offer Create, then Cross");
583 Env env{*
this, features};
604 jrr[jss::node][sfBalance.fieldName][jss::value] ==
"-0.8995000001");
610 testcase(
"Offer tfSell: Basic Sell");
615 [&](
AMM& ammAlice,
Env& env) {
625 {{
XRP(9'900),
USD(10'100)}},
634 testcase(
"Offer tfSell: 2x Sell Exceed Limit");
638 Env env{*
this, features};
640 auto const starting_xrp =
641 XRP(100) +
reserve(env, 1) + env.current()->fees().base * 2;
643 env.fund(starting_xrp,
gw,
alice);
644 env.fund(
XRP(2'000),
bob);
669 testcase(
"Client Issue: Gateway Cross Currency");
673 Env env{*
this, features};
675 auto const XTS =
gw[
"XTS"];
676 auto const XXX =
gw[
"XXX"];
678 auto const starting_xrp =
679 XRP(100.1) +
reserve(env, 1) + env.current()->fees().base * 2;
685 {XTS(100), XXX(100)},
688 AMM ammAlice(env,
alice, XTS(100), XXX(100));
692 payment[jss::id] = env.seq(
bob);
693 payment[jss::build_path] =
true;
695 payment[jss::tx_json][jss::Sequence] =
698 ->getFieldU32(sfSequence);
699 payment[jss::tx_json][jss::Fee] =
to_string(env.current()->fees().base);
700 payment[jss::tx_json][jss::SendMax] =
703 auto const jrr = env.rpc(
"json",
"submit",
to_string(payment));
704 BEAST_EXPECT(jrr[jss::result][jss::status] ==
"success");
705 BEAST_EXPECT(jrr[jss::result][jss::engine_result] ==
"tesSUCCESS");
706 if (!features[fixAMMv1_1])
708 BEAST_EXPECT(ammAlice.expectBalances(
709 STAmount(XTS, UINT64_C(101'010101010101), -12),
713 env,
bob,
STAmount{XTS, UINT64_C(98'989898989899), -12}));
717 BEAST_EXPECT(ammAlice.expectBalances(
718 STAmount(XTS, UINT64_C(101'0101010101011), -13),
722 env,
bob,
STAmount{XTS, UINT64_C(98'9898989898989), -13}));
735 Env env{*
this, features};
741 {
USD(15'000),
EUR(15'000)},
758 BEAST_EXPECT(ammAlice.expectBalances(
759 XRP(10'100),
USD(10'000), ammAlice.tokens()));
768 Env env{*
this, features};
774 {
USD(15'000),
EUR(15'000)},
793 BEAST_EXPECT(ammAlice.expectBalances(
794 XRP(10'100),
USD(10'000), ammAlice.tokens()));
802 Env env{*
this, features};
808 {
USD(15'000),
EUR(15'000)},
841 testcase(
"Combine tfSell with tfFillOrKill");
849 Env env{*
this, features};
857 ammBob.expectBalances(
XRP(20'000),
USD(200), ammBob.tokens()));
861 Env env{*
this, features};
868 BEAST_EXPECT(ammBob.expectBalances(
879 Env env{*
this, features};
886 BEAST_EXPECT(ammBob.expectBalances(
900 Env env{*
this, features};
921 [&](
AMM& ammAlice,
Env& env) {
934 {{
XRP(10'000),
USD(10'100)}},
942 [&](
AMM& ammAlice,
Env& env) {
955 {{
XRP(10'100),
USD(10'000)}},
962 Env env{*
this, features};
967 {
USD(15'000),
EUR(15'000)},
999 Env env{*
this, features};
1004 {
USD(15'000),
EUR(15'000)},
1041 Env env{*
this, features};
1085 Env env{*
this, features};
1131 using namespace jtx;
1133 Env env{*
this, features};
1135 auto const USD_bob =
bob[
"USD"];
1136 auto const f = env.current()->fees().base;
1140 AMM ammBob(env,
bob,
XRP(10'000), USD_bob(10'100));
1146 XRP(10'100), USD_bob(10'000), ammBob.
tokens()));
1159 using namespace jtx;
1161 Env env{*
this, features};
1164 auto const fee = env.current()->fees().base;
1167 auto const ann =
Account(
"ann");
1168 auto const A_BUX = ann[
"BUX"];
1170 auto const cam =
Account(
"cam");
1171 auto const dan =
Account(
"dan");
1172 auto const D_BUX = dan[
"BUX"];
1180 env(
trust(cam, D_BUX(100)));
1182 env(
pay(dan,
bob, D_BUX(100)));
1198 AMM ammBob(env,
bob, A_BUX(30), D_BUX(30));
1200 env(
trust(ann, D_BUX(100)));
1204 env(
pay(ann, ann, D_BUX(30)),
1230 using namespace jtx;
1232 Env env{*
this, features};
1234 auto const ann =
Account(
"ann");
1236 auto const cam =
Account(
"cam");
1238 auto const A_BUX = ann[
"BUX"];
1239 auto const B_BUX =
bob[
"BUX"];
1241 auto const fee = env.current()->fees().base;
1246 env(
trust(ann, B_BUX(40)));
1247 env(
trust(cam, A_BUX(40)));
1249 env(
trust(cam, B_BUX(40)));
1254 env(
pay(ann, cam, A_BUX(35)));
1255 env(
pay(
bob, cam, B_BUX(35)));
1259 AMM ammCarol(env,
carol, A_BUX(300), B_BUX(330));
1266 env.require(
balance(cam, A_BUX(35)));
1267 env.require(
balance(cam, B_BUX(35)));
1268 env.require(
offers(cam, 1));
1271 env(
offer(cam, B_BUX(30), A_BUX(30)));
1274 if (!features[fixAMMv1_1])
1277 STAmount{A_BUX, UINT64_C(309'3541659651605), -13},
1278 STAmount{B_BUX, UINT64_C(320'0215509984417), -13},
1285 STAmount{B_BUX, UINT64_C(20'0215509984417), -13},
1286 STAmount{A_BUX, UINT64_C(20'0215509984417), -13}}}}));
1291 STAmount{A_BUX, UINT64_C(309'3541659651604), -13},
1292 STAmount{B_BUX, UINT64_C(320'0215509984419), -13},
1299 STAmount{B_BUX, UINT64_C(20'0215509984419), -13},
1300 STAmount{A_BUX, UINT64_C(20'0215509984419), -13}}}}));
1309 using namespace jtx;
1311 Env env{*
this, features};
1313 auto const aliceUSD =
alice[
"USD"];
1314 auto const bobUSD =
bob[
"USD"];
1358 using namespace jtx;
1360 Env env{*
this, features};
1424 using namespace jtx;
1429 featureLendingProtocol};
1463 using namespace jtx;
1480 BEAST_EXPECT(st.
empty());
1488 BEAST_EXPECT(sa ==
XRP(100'000'000));
1492 da,
STAmount{
bob[
"USD"].issue(), UINT64_C(99'9999000001), -10}));
1502 using namespace jtx;
1505 auto const AUD =
gw[
"AUD"];
1529 using namespace jtx;
1530 auto const charlie =
Account(
"charlie");
1535 AMM ammCharlie(env, charlie,
XRP(10),
USD(11));
1538 BEAST_EXPECT(sa ==
XRP(1));
1540 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1542 auto const& pathElem = st[0][0];
1544 pathElem.isOffer() && pathElem.getIssuerID() ==
gw.
id() &&
1552 AMM ammCharlie(env, charlie,
XRP(11),
USD(10));
1556 BEAST_EXPECT(sa ==
USD(1));
1558 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1560 auto const& pathElem = st[0][0];
1562 pathElem.isOffer() &&
1572 testcase(
"Path Find: XRP -> XRP and XRP -> IOU");
1573 using namespace jtx;
1585 env.
fund(
XRP(1'000), A3, G1, G2, G3);
1589 env.
trust(G1[
"XYZ"](5'000), A1);
1590 env.
trust(G3[
"ABC"](5'000), A1);
1591 env.
trust(G2[
"XYZ"](5'000), A2);
1592 env.
trust(G3[
"ABC"](5'000), A2);
1593 env.
trust(A2[
"ABC"](1'000), A3);
1594 env.
trust(G1[
"XYZ"](100'000), M1);
1595 env.
trust(G2[
"XYZ"](100'000), M1);
1596 env.
trust(G3[
"ABC"](100'000), M1);
1599 env(
pay(G1, A1, G1[
"XYZ"](3'500)));
1600 env(
pay(G3, A1, G3[
"ABC"](1'200)));
1601 env(
pay(G1, M1, G1[
"XYZ"](25'000)));
1602 env(
pay(G2, M1, G2[
"XYZ"](25'000)));
1603 env(
pay(G3, M1, G3[
"ABC"](25'000)));
1606 AMM ammM1_G1_G2(env, M1, G1[
"XYZ"](1'000), G2[
"XYZ"](1'000));
1607 AMM ammM1_XRP_G3(env, M1,
XRP(10'000), G3[
"ABC"](1'000));
1613 auto const& send_amt =
XRP(10);
1616 BEAST_EXPECT(
equal(da, send_amt));
1617 BEAST_EXPECT(st.
empty());
1623 auto const& send_amt =
XRP(200);
1626 BEAST_EXPECT(
equal(da, send_amt));
1627 BEAST_EXPECT(st.
empty());
1631 auto const& send_amt = G3[
"ABC"](10);
1634 BEAST_EXPECT(
equal(da, send_amt));
1640 auto const& send_amt = A2[
"ABC"](1);
1643 BEAST_EXPECT(
equal(da, send_amt));
1649 auto const& send_amt = A3[
"ABC"](1);
1652 BEAST_EXPECT(
equal(da, send_amt));
1661 testcase(
"Path Find: non-XRP -> XRP");
1662 using namespace jtx;
1669 env.
fund(
XRP(1'000), A1, A2, G3);
1673 env.
trust(G3[
"ABC"](1'000), A1, A2);
1674 env.
trust(G3[
"ABC"](100'000), M1);
1677 env(
pay(G3, A1, G3[
"ABC"](1'000)));
1678 env(
pay(G3, A2, G3[
"ABC"](1'000)));
1679 env(
pay(G3, M1, G3[
"ABC"](1'200)));
1682 AMM ammM1(env, M1, G3[
"ABC"](1'000),
XRP(10'010));
1687 auto const& send_amt =
XRP(10);
1690 BEAST_EXPECT(
equal(da, send_amt));
1691 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1698 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1699 using namespace jtx;
1712 env.
fund(
XRP(1'000), A1, A2, A3, G1, G2, G3, G4);
1714 env.
fund(
XRP(21'000), M1, M2);
1717 env.
trust(G1[
"HKD"](2'000), A1);
1718 env.
trust(G2[
"HKD"](2'000), A2);
1719 env.
trust(G1[
"HKD"](2'000), A3);
1720 env.
trust(G1[
"HKD"](100'000), M1);
1721 env.
trust(G2[
"HKD"](100'000), M1);
1722 env.
trust(G1[
"HKD"](100'000), M2);
1723 env.
trust(G2[
"HKD"](100'000), M2);
1726 env(
pay(G1, A1, G1[
"HKD"](1'000)));
1727 env(
pay(G2, A2, G2[
"HKD"](1'000)));
1728 env(
pay(G1, A3, G1[
"HKD"](1'000)));
1729 env(
pay(G1, M1, G1[
"HKD"](1'200)));
1730 env(
pay(G2, M1, G2[
"HKD"](5'000)));
1731 env(
pay(G1, M2, G1[
"HKD"](1'200)));
1732 env(
pay(G2, M2, G2[
"HKD"](5'000)));
1735 AMM ammM1(env, M1, G1[
"HKD"](1'010), G2[
"HKD"](1'000));
1736 AMM ammM2XRP_G2(env, M2,
XRP(10'000), G2[
"HKD"](1'010));
1737 AMM ammM2G1_XRP(env, M2, G1[
"HKD"](1'010),
XRP(10'000));
1745 auto const& send_amt = G1[
"HKD"](10);
1747 env, A1, G1, send_amt,
std::nullopt, G1[
"HKD"].currency);
1748 BEAST_EXPECT(st.
empty());
1749 BEAST_EXPECT(
equal(da, send_amt));
1750 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1756 auto const& send_amt = A1[
"HKD"](10);
1758 env, A1, G1, send_amt,
std::nullopt, G1[
"HKD"].currency);
1759 BEAST_EXPECT(st.
empty());
1760 BEAST_EXPECT(
equal(da, send_amt));
1761 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1767 auto const& send_amt = A3[
"HKD"](10);
1769 env, A1, A3, send_amt,
std::nullopt, G1[
"HKD"].currency);
1770 BEAST_EXPECT(
equal(da, send_amt));
1771 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1778 auto const& send_amt = G2[
"HKD"](10);
1780 env, G1, G2, send_amt,
std::nullopt, G1[
"HKD"].currency);
1781 BEAST_EXPECT(
equal(da, send_amt));
1782 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1794 auto const& send_amt = G2[
"HKD"](10);
1796 env, A1, G2, send_amt,
std::nullopt, G1[
"HKD"].currency);
1797 BEAST_EXPECT(
equal(da, send_amt));
1798 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1811 auto const& send_amt = A2[
"HKD"](10);
1813 env, A1, A2, send_amt,
std::nullopt, G1[
"HKD"].currency);
1814 BEAST_EXPECT(
equal(da, send_amt));
1815 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1828 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1829 using namespace jtx;
1839 env.
fund(
XRP(1'000), A1, A2, A3, G1, G2);
1842 env.
trust(G1[
"HKD"](2'000), A1);
1843 env.
trust(G2[
"HKD"](2'000), A2);
1844 env.
trust(A2[
"HKD"](2'000), A3);
1845 env.
trust(G1[
"HKD"](100'000), M1);
1846 env.
trust(G2[
"HKD"](100'000), M1);
1849 env(
pay(G1, A1, G1[
"HKD"](1'000)));
1850 env(
pay(G2, A2, G2[
"HKD"](1'000)));
1851 env(
pay(G1, M1, G1[
"HKD"](5'000)));
1852 env(
pay(G2, M1, G2[
"HKD"](5'000)));
1855 AMM ammM1(env, M1, G1[
"HKD"](1'010), G2[
"HKD"](1'000));
1859 auto const& send_amt = A2[
"HKD"](10);
1864 BEAST_EXPECT(
equal(da, send_amt));
1865 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1874 using namespace jtx;
1876 Env env(*
this, features);
1882 auto const AMMXRPPool = env.
current()->fees().increment * 2;
1899 AMM ammBob(env,
bob, AMMXRPPool,
USD(150));
1907 BEAST_EXPECT(carolUSD >
USD(0) && carolUSD <
USD(50));
1915 using namespace jtx;
1919 Env env(*
this, features);
1938 ammBob.expectBalances(
BTC(150),
USD(100), ammBob.tokens()));
1942 Env env(*
this, features);
1961 BEAST_EXPECT(ammBobBTC_XRP.expectBalances(
1962 BTC(150),
XRP(100), ammBobBTC_XRP.tokens()));
1968 Env env(*
this, features);
1989 ammBob.expectBalances(
XRP(150),
USD(100), ammBob.tokens()));
1993 Env env(*
this, features);
2013 ammBob.expectBalances(
USD(150),
XRP(100), ammBob.tokens()));
2017 Env env(*
this, features);
2070 Env env(*
this, features);
2104 auto const flowResult = [&] {
2119 paths.push_back(p1);
2122 paths.push_back(p2);
2141 BEAST_EXPECT(flowResult.removableOffers.size() == 1);
2144 if (flowResult.removableOffers.empty())
2147 for (
auto const& o : flowResult.removableOffers)
2170 Env env(*
this, features);
2193 BEAST_EXPECT(ammBob.expectBalances(
2202 using namespace jtx;
2206 Env env(*
this, features);
2213 {
USD(1'000),
GBP(1'000)});
2228 if (!features[fixAMMv1_1])
2231 BEAST_EXPECT(amm.expectBalances(
2238 BEAST_EXPECT(amm.expectBalances(
2251 Env env(*
this, features);
2285 BEAST_EXPECT(amm.expectBalances(
2295 Env env(*
this, features);
2308 AMM amm2(env, ed,
EUR(1'000),
USD(1'000));
2317 if (!features[fixAMMv1_1])
2355 Env env(*
this, features);
2367 amm.expectBalances(
USD(1'100),
EUR(1'000), amm.tokens()));
2375 Env env(*
this, features);
2382 {
USD(1'000),
GBP(1'000)});
2402 BEAST_EXPECT(amm.expectBalances(
2403 STAmount{GBP, UINT64_C(1'142'857142857143), -12},
2413 Env env(*
this, features);
2420 {
USD(1'200),
GBP(1'200)});
2434 if (!features[fixAMMv1_1])
2441 BEAST_EXPECT(amm.expectBalances(
2442 GBP(1'024),
USD(1'171.875), amm.tokens()));
2452 STAmount{
GBP, UINT64_C(1'169'999999999999), -12}));
2454 BEAST_EXPECT(amm.expectBalances(
2455 STAmount{GBP, UINT64_C(1'024'000000000001), -12},
2466 Env env(*
this, features);
2491 if (!features[fixAMMv1_1])
2499 STAmount{
GBP, UINT64_C(1'311'973684210527), -12}));
2506 STAmount{
GBP, UINT64_C(1'470'421052631579), -12}));
2513 STAmount{
EUR, UINT64_C(929'5789473684212), -13}}}));
2516 BEAST_EXPECT(amm.expectBalances(
2517 STAmount{EUR, UINT64_C(1'056'336842105263), -12},
2518 STAmount{USD, UINT64_C(1'325'334821428571), -12},
2529 STAmount{
GBP, UINT64_C(1'311'973684210525), -12}));
2536 STAmount{
GBP, UINT64_C(1'470'42105263158), -11}));
2543 STAmount{
EUR, UINT64_C(929'57894736842), -11}}}));
2546 BEAST_EXPECT(amm.expectBalances(
2547 STAmount{EUR, UINT64_C(1'056'336842105264), -12},
2548 STAmount{USD, UINT64_C(1'325'334821428571), -12},
2558 Env env(*
this, features);
2583 if (!features[fixAMMv1_1])
2591 STAmount{
GBP, UINT64_C(1'329'578947368421), -12}));
2595 BEAST_EXPECT(amm.expectBalances(
2596 STAmount{GBP, UINT64_C(1'056'336842105263), -12},
2597 STAmount{EUR, UINT64_C(946'6677295918366), -13},
2608 STAmount{
GBP, UINT64_C(1'329'57894736842), -11}));
2612 BEAST_EXPECT(amm.expectBalances(
2613 STAmount{GBP, UINT64_C(1'056'336842105264), -12},
2614 STAmount{EUR, UINT64_C(946'6677295918366), -13},
2623 STAmount{
EUR, UINT64_C(1'442'665816326531), -12}));
2630 STAmount{
USD, UINT64_C(1'340'267857142857), -12}}}));
2638 Env env(*
this, features);
2651 AMM amm2(env, ed,
EUR(1'000),
USD(1'400));
2661 if (!features[fixAMMv1_1])
2669 STAmount{
GBP, UINT64_C(1'292'469135802469), -12}));
2672 STAmount{GBP, UINT64_C(1'086'024691358025), -12},
2673 STAmount{EUR, UINT64_C(920'78937795562), -11},
2678 STAmount{EUR, UINT64_C(1'063'368497635504), -12},
2679 STAmount{USD, UINT64_C(1'316'570881226053), -12},
2690 STAmount{
GBP, UINT64_C(1'292'469135802466), -12}));
2693 STAmount{GBP, UINT64_C(1'086'024691358027), -12},
2694 STAmount{EUR, UINT64_C(920'7893779556188), -13},
2699 STAmount{EUR, UINT64_C(1'063'368497635505), -12},
2700 STAmount{USD, UINT64_C(1'316'570881226053), -12},
2710 Env env(*
this, features);
2732 if (!features[fixAMMv1_1])
2736 STAmount{GBP, UINT64_C(1'108'148148148149), -12},
2737 STAmount{EUR, UINT64_C(902'4064171122988), -13},
2742 STAmount{EUR, UINT64_C(1'078'074866310161), -12},
2743 STAmount{USD, UINT64_C(1'298'611111111111), -12},
2750 STAmount{GBP, UINT64_C(1'108'148148148151), -12},
2751 STAmount{EUR, UINT64_C(902'4064171122975), -13},
2756 STAmount{EUR, UINT64_C(1'078'074866310162), -12},
2757 STAmount{USD, UINT64_C(1'298'611111111111), -12},
2774 using namespace jtx;
2790 ammBob.expectBalances(
XRP(1'050),
USD(1'000), ammBob.tokens()));
2801 using namespace jtx;
2851 auto const JPY =
gw[
"JPY"];
2860 {
USD(200),
EUR(200), JPY(200)},
2865 AMM ammAliceXRP_JPY(env,
alice,
XRP(100), JPY(100));
2880 using namespace jtx;
2881 Env env(*
this, features);
2882 auto const dan =
Account(
"dan");
2883 auto const ed =
Account(
"ed");
2899 if (!features[fixAMMv1_1])
2925 testcase(
"Convert all of an asset using DeliverMin");
2927 using namespace jtx;
2930 Env env(*
this, features);
2962 drops(10'000'000'000 - env.
current()->fees().base.drops())));
2967 Env env(*
this, features);
2981 Env env(*
this, features);
3003 auto const dan =
Account(
"dan");
3004 Env env(*
this, features);
3012 AMM ammDan(env, dan,
XRP(1'000),
USD(1'100));
3013 if (!features[fixAMMv1_1])
3037 STAmount{USD, UINT64_C(999'99999909091), -11},
3048 using namespace jtx;
3054 Env env(*
this, features);
3079 env(
pay(becky, becky,
USD(10)),
3094 using namespace jtx;
3115 auto failedIouPayments = [
this, &env]() {
3139 failedIouPayments();
3156 failedIouPayments();
3163 failedIouPayments();
3186 using namespace test::jtx;
3187 Env env(*
this, features);
3200 env(
pay(G1,
bob, G1[
"USD"](10)));
3201 env(
pay(G1,
alice, G1[
"USD"](205)));
3204 AMM ammAlice(env,
alice,
XRP(500), G1[
"USD"](105));
3210 BEAST_EXPECT(
lines[jss::lines][0u][jss::account] == G1.human());
3211 BEAST_EXPECT(
lines[jss::lines][0u][jss::limit] ==
"100");
3212 BEAST_EXPECT(
lines[jss::lines][0u][jss::balance] ==
"10");
3219 BEAST_EXPECT(
lines[jss::lines][0u][jss::account] == G1.human());
3220 BEAST_EXPECT(
lines[jss::lines][0u][jss::limit] ==
"205");
3222 BEAST_EXPECT(
lines[jss::lines][0u][jss::balance] ==
"100");
3244 XRP(525), G1[
"USD"](100), ammAlice.
tokens()));
3263 for (
auto const& it :
lines[jss::lines])
3265 if (it[jss::account] ==
bob.
human())
3271 if (!BEAST_EXPECT(bobLine))
3273 BEAST_EXPECT(bobLine[jss::freeze] ==
true);
3274 BEAST_EXPECT(bobLine[jss::balance] ==
"-16");
3281 for (
auto const& it :
lines[jss::lines])
3283 if (it[jss::account] == G1.human())
3289 if (!BEAST_EXPECT(g1Line))
3291 BEAST_EXPECT(g1Line[jss::freeze_peer] ==
true);
3292 BEAST_EXPECT(g1Line[jss::balance] ==
"16");
3299 auto affected = env.
meta()->getJson(
3304 affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
3306 ff[sfLowLimit.fieldName] ==
3308 BEAST_EXPECT(!(ff[jss::Flags].asUInt() &
lsfLowFreeze));
3319 using namespace test::jtx;
3320 Env env(*
this, features);
3330 env.
fund(
XRP(20'000), A2, A3, A4);
3333 env.
trust(G1[
"USD"](1'200), A1);
3334 env.
trust(G1[
"USD"](200), A2);
3335 env.
trust(G1[
"BTC"](100), A3);
3336 env.
trust(G1[
"BTC"](100), A4);
3339 env(
pay(G1, A1, G1[
"USD"](1'000)));
3340 env(
pay(G1, A2, G1[
"USD"](100)));
3341 env(
pay(G1, A3, G1[
"BTC"](100)));
3342 env(
pay(G1, A4, G1[
"BTC"](100)));
3345 AMM ammG1(env, G1,
XRP(10'000), G1[
"USD"](100));
3357 "XRP")[jss::result][jss::offers];
3365 BEAST_EXPECT(accounts.
find(A2.human()) !=
std::end(accounts));
3371 std::string(
"USD/") + G1.human())[jss::result][jss::offers];
3379 BEAST_EXPECT(accounts.
find(A1.human()) !=
std::end(accounts));
3386 AMM ammA3(env, A3, G1[
"BTC"](1),
XRP(1));
3392 env(
pay(G1, A2, G1[
"USD"](1)));
3395 env(
pay(A2, G1, G1[
"USD"](1)));
3398 env(
pay(A2, A1, G1[
"USD"](1)));
3401 env(
pay(A1, A2, G1[
"USD"](1)));
3427 std::string(
"USD/") + G1.human())[jss::result][jss::offers];
3434 "XRP")[jss::result][jss::offers];
3442 env(
pay(G1, A2, G1[
"USD"](1)));
3445 env(
pay(A2, G1, G1[
"USD"](1)));
3455 testcase(
"Offers for Frozen Trust Lines");
3457 using namespace test::jtx;
3458 Env env(*
this, features);
3465 env.
fund(
XRP(2'000), G1, A3, A4);
3469 env.
trust(G1[
"USD"](1'000), A2);
3470 env.
trust(G1[
"USD"](2'000), A3);
3471 env.
trust(G1[
"USD"](2'001), A4);
3474 env(
pay(G1, A3, G1[
"USD"](2'000)));
3475 env(
pay(G1, A4, G1[
"USD"](2'001)));
3478 AMM ammA3(env, A3,
XRP(1'000), G1[
"USD"](1'001));
3489 env(
offer(A4,
XRP(999), G1[
"USD"](999)));
3500 BEAST_EXPECT(info[jss::amm][jss::asset2_frozen].asBool());
3516 env(
offer(A2, G1[
"USD"](999),
XRP(999)));
3528 testcase(
"Multisign AMM Transactions");
3530 using namespace jtx;
3531 Env env{*
this, features};
3548 msig const ms{becky, bogie};
3574 ammAlice.
vote({}, 1'000);
3577 env(ammAlice.
bid({.account = alice, .bidMin = 100}), ms).close();
3589 using namespace jtx;
3593 Env env(*
this, features);
3600 {
USD(2'000),
EUR(1'000)});
3616 using namespace jtx;
3620 Env env(*
this, features);
3621 auto const BobUSD =
bob[
"USD"];
3622 auto const BobEUR =
bob[
"EUR"];
3631 {BobUSD(100), BobEUR(100)},
3635 AMM ammBobXRP_USD(env,
bob,
XRP(100), BobUSD(100));
3638 AMM ammBobUSD_EUR(env,
bob, BobUSD(100), BobEUR(100));
3641 Path const p = [&] {
3658 Env env(*
this, features);
3672 Env env(*
this, features);
3691 using namespace jtx;
3693 auto const CNY =
gw[
"CNY"];
3696 Env env(*
this, features);
3717 Env env(*
this, features);
3731 AMM ammBobEUR_CNY(env,
bob,
EUR(100), CNY(100));
3757 using namespace jtx;
3762 featureLendingProtocol};
3775 using namespace jtx;
3780 featureLendingProtocol};
3788 using namespace jtx;
3793 featureLendingProtocol};
3805 featureLendingProtocol};
3813 using namespace test::jtx;
3818 featureLendingProtocol};
3855BEAST_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