1#include <test/jtx/AMM.h>
2#include <test/jtx/AMMTest.h>
3#include <test/jtx/Account.h>
4#include <test/jtx/Env.h>
5#include <test/jtx/PathSet.h>
6#include <test/jtx/TestHelpers.h>
7#include <test/jtx/amount.h>
8#include <test/jtx/balance.h>
9#include <test/jtx/delivermin.h>
10#include <test/jtx/fee.h>
11#include <test/jtx/flags.h>
12#include <test/jtx/jtx_json.h>
13#include <test/jtx/multisign.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/quality.h>
20#include <test/jtx/rate.h>
21#include <test/jtx/regkey.h>
22#include <test/jtx/require.h>
23#include <test/jtx/sendmax.h>
24#include <test/jtx/sig.h>
25#include <test/jtx/tags.h>
26#include <test/jtx/ter.h>
27#include <test/jtx/trust.h>
28#include <test/jtx/txflags.h>
30#include <xrpl/basics/Number.h>
31#include <xrpl/beast/unit_test/suite.h>
32#include <xrpl/json/json_value.h>
33#include <xrpl/json/to_string.h>
34#include <xrpl/ledger/ApplyView.h>
35#include <xrpl/ledger/OpenView.h>
36#include <xrpl/ledger/PaymentSandbox.h>
37#include <xrpl/ledger/Sandbox.h>
38#include <xrpl/ledger/helpers/OfferHelpers.h>
39#include <xrpl/protocol/AccountID.h>
40#include <xrpl/protocol/Feature.h>
41#include <xrpl/protocol/Indexes.h>
42#include <xrpl/protocol/Issue.h>
43#include <xrpl/protocol/KeyType.h>
44#include <xrpl/protocol/LedgerFormats.h>
45#include <xrpl/protocol/SField.h>
46#include <xrpl/protocol/STPathSet.h>
47#include <xrpl/protocol/Seed.h>
48#include <xrpl/protocol/TER.h>
49#include <xrpl/protocol/TxFlags.h>
50#include <xrpl/protocol/UintTypes.h>
51#include <xrpl/protocol/XRPAmount.h>
52#include <xrpl/protocol/jss.h>
53#include <xrpl/tx/paths/Flow.h>
54#include <xrpl/tx/paths/detail/Steps.h>
82 testcase(
"Incorrect Removal of Funded Offers");
95 Env env{*
this, features};
120 if (!features[fixAMMv1_1])
123 STAmount{BTC, UINT64_C(1'001'000000374812), -12},
USD(100'000), ammCarol.
tokens()));
128 STAmount{BTC, UINT64_C(1'001'000000374815), -12},
USD(100'000), ammCarol.
tokens()));
143 Env env{*
this, features};
148 auto const usD1 = gw1[
"USD"];
149 auto const usD2 = gw2[
"USD"];
154 env(
trust(
bob_, usD1(1'000), tfSetNoRipple));
156 env(
trust(
bob_, usD2(1'000), tfSetNoRipple));
159 env(
pay(gw1, dan, usD1(10'000)));
164 AMM const ammDan(env, dan,
XRP(10'000), usD1(10'000));
175 Env env{*
this, features};
180 auto const usD1 = gw1[
"USD"];
181 auto const usD2 = gw2[
"USD"];
190 env(
pay(gw1, dan, usD1(10'050)));
195 AMM const ammDan(env, dan,
XRP(10'000), usD1(10'050));
216 auto const startBalance =
XRP(1'000'000);
222 [&](
AMM& ammAlice,
Env& env) {
241 {{
XRP(10'100),
USD(10'000)}},
249 [&](
AMM& ammAlice,
Env& env) {
264 {{
XRP(10'100),
USD(10'000)}},
270 if (features[featureMPTokensV2])
272 Env env{*
this, features};
283 ammAlice.expectBalances(
XRP(10'100'000),
USD(10'000'000), ammAlice.tokens()));
289 [&](
AMM& ammAlice,
Env& env) {
298 {{
XRP(10'100),
USD(10'000)}},
306 [&](
AMM& ammAlice,
Env& env) {
321 {{
XRP(11'000),
USD(9'000)}},
330 testcase(
"Offer Crossing with XRP, Normal order");
334 Env env{*
this, features};
343 auto const xrpTransferred =
XRPAmount{3'061'224'490};
346 BEAST_EXPECT(ammAlice.expectBalances(
347 XRP(150'000) + xrpTransferred,
USD(49),
IOUAmount{273'861'278752583, -8}));
358 testcase(
"Offer Crossing with Limit Override");
362 Env env{*
this, features};
377 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] ==
"-1");
380 jrr[jss::node][sfBalance.fieldName] ==
387 testcase(
"Currency Conversion: Entire Offer");
391 Env env{*
this, features};
412 jrr[jss::node][sfBalance.fieldName] ==
419 testcase(
"Currency Conversion: In Parts");
424 [&](
AMM& ammAlice,
Env& env) {
444 {{
XRP(10'000),
USD(10'000)}},
453 testcase(
"Cross Currency Payment: Start with XRP");
458 [&](
AMM& ammAlice,
Env& env) {
467 {{
XRP(10'000),
USD(10'100)}},
476 testcase(
"Cross Currency Payment: End with XRP");
481 [&](
AMM& ammAlice,
Env& env) {
491 {{
XRP(10'100),
USD(10'000)}},
500 testcase(
"Cross Currency Payment: Bridged");
504 Env env{*
this, features};
506 auto const gw1 =
Account{
"gateway_1"};
507 auto const gw2 =
Account{
"gateway_2"};
508 auto const dan =
Account{
"dan"};
509 auto const usD1 = gw1[
"USD"];
510 auto const euR1 = gw2[
"EUR"];
519 env(
trust(dan, euR1(1'000)));
525 env(
pay(gw2, dan, dan[
"EUR"](400)));
528 AMM const ammCarol(env,
carol_, usD1(5'000),
XRP(50'000));
530 env(
offer(dan,
XRP(500), euR1(50)));
534 jtp[0u][0u][jss::currency] =
"XRP";
538 XRP(49'700),
STAmount{usD1, UINT64_C(5'030'181086519115), -12}, ammCarol.
tokens()));
546 testcase(
"Offer Fees Consume Funds");
550 Env env{*
this, features};
552 auto const gw1 =
Account{
"gateway_1"};
553 auto const gw2 =
Account{
"gateway_2"};
554 auto const gw3 =
Account{
"gateway_3"};
555 auto const localAlice =
Account{
"alice"};
556 auto const localBob =
Account{
"bob"};
557 auto const usD1 = gw1[
"USD"];
558 auto const usD2 = gw2[
"USD"];
559 auto const usD3 = gw3[
"USD"];
567 auto const startingXrp =
568 XRP(100) + env.
current()->fees().accountReserve(3, 1) + env.
current()->fees().base * 4;
570 env.
fund(startingXrp, gw1, gw2, gw3, localAlice);
571 env.
fund(
XRP(2'000), localBob);
574 env(
trust(localAlice, usD1(1'000)));
575 env(
trust(localAlice, usD2(1'000)));
576 env(
trust(localAlice, usD3(1'000)));
577 env(
trust(localBob, usD1(1'200)));
578 env(
trust(localBob, usD2(1'100)));
580 env(
pay(gw1, localBob, localBob[
"USD"](1'200)));
582 AMM const ammBob(env, localBob,
XRP(1'000), usD1(1'200));
585 env(
offer(localAlice, usD1(200),
XRP(200)));
590 XRP(1'100),
STAmount{usD1, UINT64_C(1'090'909090909091), -12}, ammBob.
tokens()));
593 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] ==
"109.090909090909");
595 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName] ==
XRP(350).value().getText());
601 testcase(
"Offer Create, then Cross");
605 Env env{*
this, features};
624 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] ==
"-0.8995000001");
630 testcase(
"Offer tfSell: Basic Sell");
635 [&](
AMM& ammAlice,
Env& env) {
644 {{
XRP(9'900),
USD(10'100)}},
653 testcase(
"Offer tfSell: 2x Sell Exceed Limit");
657 Env env{*
this, features};
659 auto const startingXrp =
XRP(100) +
reserve(env, 1) + env.
current()->fees().base * 2;
686 testcase(
"Client Issue: Gateway Cross Currency");
690 Env env{*
this, features};
692 auto const xts =
gw_[
"XTS"];
693 auto const xxx =
gw_[
"XXX"];
695 auto const startingXrp =
XRP(100.1) +
reserve(env, 1) + env.
current()->fees().base * 2;
698 AMM const ammAlice(env,
alice_, xts(100), xxx(100));
702 payment[jss::id] = env.
seq(
bob_);
703 payment[jss::build_path] =
true;
705 payment[jss::tx_json][jss::Sequence] =
708 payment[jss::tx_json][jss::SendMax] =
710 payment[jss::tx_json][jss::Flags] = tfPartialPayment;
711 auto const jrr = env.
rpc(
"json",
"submit",
to_string(payment));
712 BEAST_EXPECT(jrr[jss::result][jss::status] ==
"success");
713 BEAST_EXPECT(jrr[jss::result][jss::engine_result] ==
"tesSUCCESS");
714 if (!features[fixAMMv1_1])
716 BEAST_EXPECT(ammAlice.expectBalances(
717 STAmount(xts, UINT64_C(101'010101010101), -12), xxx(99), ammAlice.tokens()));
722 BEAST_EXPECT(ammAlice.expectBalances(
723 STAmount(xts, UINT64_C(101'0101010101011), -13), xxx(99), ammAlice.tokens()));
737 Env env{*
this, features};
755 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100),
USD(10'000), ammAlice.tokens()));
763 Env env{*
this, features};
783 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'100),
USD(10'000), ammAlice.tokens()));
791 Env env{*
this, features};
824 testcase(
"Combine tfSell with tfFillOrKill");
832 Env env{*
this, features};
838 BEAST_EXPECT(ammBob.expectBalances(
XRP(20'000),
USD(200), ammBob.tokens()));
842 Env env{*
this, features};
849 BEAST_EXPECT(ammBob.expectBalances(
850 XRP(20'220),
STAmount{
USD, UINT64_C(197'8239366963403), -13}, ammBob.tokens()));
858 Env env{*
this, features};
865 BEAST_EXPECT(ammBob.expectBalances(
866 XRP(21'500),
STAmount{
USD, UINT64_C(186'046511627907), -12}, ammBob.tokens()));
877 Env env{*
this, features};
897 [&](
AMM& ammAlice,
Env& env) {
909 {{
XRP(10'000),
USD(10'100)}},
917 [&](
AMM& ammAlice,
Env& env) {
929 {{
XRP(10'100),
USD(10'000)}},
936 Env env{*
this, features};
967 Env env{*
this, features};
1002 Env env{*
this, features};
1045 Env env{*
this, features};
1090 using namespace jtx;
1092 Env env{*
this, features};
1094 auto const usdBob =
bob_[
"USD"];
1095 auto const f = env.
current()->fees().base;
1099 AMM const ammBob(env,
bob_,
XRP(10'000), usdBob(10'100));
1117 using namespace jtx;
1119 Env env{*
this, features};
1122 auto const fee = env.
current()->fees().base;
1125 auto const ann =
Account(
"ann");
1126 auto const aBux = ann[
"BUX"];
1127 auto const localBob =
Account(
"bob");
1128 auto const cam =
Account(
"cam");
1129 auto const dan =
Account(
"dan");
1130 auto const dBux = dan[
"BUX"];
1133 env.
fund(
reserve(env, 4) + (fee * 4), ann, localBob, cam, dan);
1136 env(
trust(localBob, aBux(400)));
1138 env(
trust(cam, dBux(100)));
1140 env(
pay(dan, localBob, dBux(100)));
1144 env(
pay(ann, cam, dBux(60)),
Path(localBob, dan),
Sendmax(aBux(200)));
1156 AMM const ammBob(env, localBob, aBux(30), dBux(30));
1158 env(
trust(ann, dBux(100)));
1184 using namespace jtx;
1186 Env env{*
this, features};
1188 auto const ann =
Account(
"ann");
1189 auto const localBob =
Account(
"bob");
1190 auto const cam =
Account(
"cam");
1191 auto const carol =
Account(
"carol");
1192 auto const aBux = ann[
"BUX"];
1193 auto const bBux = localBob[
"BUX"];
1195 auto const fee = env.
current()->fees().base;
1197 env.
fund(
reserve(env, 4) + (fee * 5), ann, localBob, cam);
1200 env(
trust(ann, bBux(40)));
1201 env(
trust(cam, aBux(40)));
1202 env(
trust(localBob, aBux(30)));
1203 env(
trust(cam, bBux(40)));
1204 env(
trust(carol, bBux(400)));
1205 env(
trust(carol, aBux(400)));
1208 env(
pay(ann, cam, aBux(35)));
1209 env(
pay(localBob, cam, bBux(35)));
1210 env(
pay(localBob, carol, bBux(400)));
1211 env(
pay(ann, carol, aBux(400)));
1213 AMM const ammCarol(env, carol, aBux(300), bBux(330));
1218 env(
offer(cam, aBux(29), bBux(30), tfPassive));
1225 env(
offer(cam, bBux(30), aBux(30)));
1228 if (!features[fixAMMv1_1])
1231 STAmount{aBux, UINT64_C(309'3541659651605), -13},
1232 STAmount{bBux, UINT64_C(320'0215509984417), -13},
1239 STAmount{bBux, UINT64_C(20'0215509984417), -13},
1240 STAmount{aBux, UINT64_C(20'0215509984417), -13}}}}));
1245 STAmount{aBux, UINT64_C(309'3541659651604), -13},
1246 STAmount{bBux, UINT64_C(320'0215509984419), -13},
1253 STAmount{bBux, UINT64_C(20'0215509984419), -13},
1254 STAmount{aBux, UINT64_C(20'0215509984419), -13}}}}));
1263 using namespace jtx;
1265 Env env{*
this, features};
1267 auto const aliceUSD =
alice_[
"USD"];
1268 auto const bobUSD =
bob_[
"USD"];
1274 env(
fset(
gw_, asfRequireAuth));
1311 using namespace jtx;
1313 Env env{*
this, features};
1323 env(
fset(
gw_, asfRequireAuth));
1375 using namespace jtx;
1410 using namespace jtx;
1423 BEAST_EXPECT(st.
empty());
1427 BEAST_EXPECT(sa ==
XRP(100'000'000));
1440 using namespace jtx;
1443 auto const aud =
gw_[
"AUD"];
1458 BEAST_EXPECT(std::get<0>(result).empty());
1465 using namespace jtx;
1466 auto const charlie =
Account(
"charlie");
1471 AMM const ammCharlie(env, charlie,
XRP(10),
USD(11));
1473 BEAST_EXPECT(sa ==
XRP(1));
1475 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1477 auto const& pathElem = st[0][0];
1479 pathElem.isOffer() && pathElem.getIssuerID() ==
gw_.id() &&
1480 pathElem.getCurrency() ==
USD.currency);
1487 AMM const ammCharlie(env, charlie,
XRP(11),
USD(10));
1490 BEAST_EXPECT(sa ==
USD(1));
1492 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1494 auto const& pathElem = st[0][0];
1496 pathElem.isOffer() && pathElem.getIssuerID() ==
xrpAccount() &&
1505 testcase(
"Path Find: XRP -> XRP and XRP -> IOU");
1506 using namespace jtx;
1518 env.
fund(
XRP(1'000), a3, g1, g2, g3);
1522 env.
trust(g1[
"XYZ"](5'000), a1);
1523 env.
trust(g3[
"ABC"](5'000), a1);
1524 env.
trust(g2[
"XYZ"](5'000), a2);
1525 env.
trust(g3[
"ABC"](5'000), a2);
1526 env.
trust(a2[
"ABC"](1'000), a3);
1527 env.
trust(g1[
"XYZ"](100'000), m1);
1528 env.
trust(g2[
"XYZ"](100'000), m1);
1529 env.
trust(g3[
"ABC"](100'000), m1);
1532 env(
pay(g1, a1, g1[
"XYZ"](3'500)));
1533 env(
pay(g3, a1, g3[
"ABC"](1'200)));
1534 env(
pay(g1, m1, g1[
"XYZ"](25'000)));
1535 env(
pay(g2, m1, g2[
"XYZ"](25'000)));
1536 env(
pay(g3, m1, g3[
"ABC"](25'000)));
1539 AMM const ammM1G1G2(env, m1, g1[
"XYZ"](1'000), g2[
"XYZ"](1'000));
1540 AMM const ammM1XrpG3(env, m1,
XRP(10'000), g3[
"ABC"](1'000));
1546 auto const& sendAmt =
XRP(10);
1548 BEAST_EXPECT(
equal(da, sendAmt));
1549 BEAST_EXPECT(st.
empty());
1555 auto const& sendAmt =
XRP(200);
1558 BEAST_EXPECT(
equal(da, sendAmt));
1559 BEAST_EXPECT(st.
empty());
1563 auto const& sendAmt = g3[
"ABC"](10);
1565 BEAST_EXPECT(
equal(da, sendAmt));
1571 auto const& sendAmt = a2[
"ABC"](1);
1573 BEAST_EXPECT(
equal(da, sendAmt));
1579 auto const& sendAmt = a3[
"ABC"](1);
1581 BEAST_EXPECT(
equal(da, sendAmt));
1590 testcase(
"Path Find: non-XRP -> XRP");
1591 using namespace jtx;
1598 env.
fund(
XRP(1'000), a1, a2, g3);
1602 env.
trust(g3[
"ABC"](1'000), a1, a2);
1603 env.
trust(g3[
"ABC"](100'000), m1);
1606 env(
pay(g3, a1, g3[
"ABC"](1'000)));
1607 env(
pay(g3, a2, g3[
"ABC"](1'000)));
1608 env(
pay(g3, m1, g3[
"ABC"](1'200)));
1611 AMM const ammM1(env, m1, g3[
"ABC"](1'000),
XRP(10'010));
1616 auto const& sendAmt =
XRP(10);
1617 std::tie(st, sa, da) =
findPaths(env, a1, a2, sendAmt, std::nullopt, a2[
"ABC"].currency);
1618 BEAST_EXPECT(
equal(da, sendAmt));
1619 BEAST_EXPECT(
equal(sa, a1[
"ABC"](1)));
1626 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1627 using namespace jtx;
1640 env.
fund(
XRP(1'000), a1, a2, a3, g1, g2, g3, g4);
1642 env.
fund(
XRP(21'000), m1, m2);
1645 env.
trust(g1[
"HKD"](2'000), a1);
1646 env.
trust(g2[
"HKD"](2'000), a2);
1647 env.
trust(g1[
"HKD"](2'000), a3);
1648 env.
trust(g1[
"HKD"](100'000), m1);
1649 env.
trust(g2[
"HKD"](100'000), m1);
1650 env.
trust(g1[
"HKD"](100'000), m2);
1651 env.
trust(g2[
"HKD"](100'000), m2);
1654 env(
pay(g1, a1, g1[
"HKD"](1'000)));
1655 env(
pay(g2, a2, g2[
"HKD"](1'000)));
1656 env(
pay(g1, a3, g1[
"HKD"](1'000)));
1657 env(
pay(g1, m1, g1[
"HKD"](1'200)));
1658 env(
pay(g2, m1, g2[
"HKD"](5'000)));
1659 env(
pay(g1, m2, g1[
"HKD"](1'200)));
1660 env(
pay(g2, m2, g2[
"HKD"](5'000)));
1663 AMM const ammM1(env, m1, g1[
"HKD"](1'010), g2[
"HKD"](1'000));
1664 AMM const ammM2XrpG2(env, m2,
XRP(10'000), g2[
"HKD"](1'010));
1665 AMM const ammM2G1Xrp(env, m2, g1[
"HKD"](1'010),
XRP(10'000));
1673 auto const& sendAmt = g1[
"HKD"](10);
1675 findPaths(env, a1, g1, sendAmt, std::nullopt, g1[
"HKD"].currency);
1676 BEAST_EXPECT(st.
empty());
1677 BEAST_EXPECT(
equal(da, sendAmt));
1678 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1684 auto const& sendAmt = a1[
"HKD"](10);
1686 findPaths(env, a1, g1, sendAmt, std::nullopt, g1[
"HKD"].currency);
1687 BEAST_EXPECT(st.
empty());
1688 BEAST_EXPECT(
equal(da, sendAmt));
1689 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1695 auto const& sendAmt = a3[
"HKD"](10);
1697 findPaths(env, a1, a3, sendAmt, std::nullopt, g1[
"HKD"].currency);
1698 BEAST_EXPECT(
equal(da, sendAmt));
1699 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1706 auto const& sendAmt = g2[
"HKD"](10);
1708 findPaths(env, g1, g2, sendAmt, std::nullopt, g1[
"HKD"].currency);
1709 BEAST_EXPECT(
equal(da, sendAmt));
1710 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1722 auto const& sendAmt = g2[
"HKD"](10);
1724 findPaths(env, a1, g2, sendAmt, std::nullopt, g1[
"HKD"].currency);
1725 BEAST_EXPECT(
equal(da, sendAmt));
1726 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1739 auto const& sendAmt = a2[
"HKD"](10);
1741 findPaths(env, a1, a2, sendAmt, std::nullopt, g1[
"HKD"].currency);
1742 BEAST_EXPECT(
equal(da, sendAmt));
1743 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1756 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1757 using namespace jtx;
1767 env.
fund(
XRP(1'000), a1, a2, a3, g1, g2);
1770 env.
trust(g1[
"HKD"](2'000), a1);
1771 env.
trust(g2[
"HKD"](2'000), a2);
1772 env.
trust(a2[
"HKD"](2'000), a3);
1773 env.
trust(g1[
"HKD"](100'000), m1);
1774 env.
trust(g2[
"HKD"](100'000), m1);
1777 env(
pay(g1, a1, g1[
"HKD"](1'000)));
1778 env(
pay(g2, a2, g2[
"HKD"](1'000)));
1779 env(
pay(g1, m1, g1[
"HKD"](5'000)));
1780 env(
pay(g2, m1, g2[
"HKD"](5'000)));
1783 AMM const ammM1(env, m1, g1[
"HKD"](1'010), g2[
"HKD"](1'000));
1787 auto const& sendAmt = a2[
"HKD"](10);
1790 std::tie(st, sa, da) =
findPaths(env, g1, a2, sendAmt, std::nullopt, g1[
"HKD"].currency);
1791 BEAST_EXPECT(
equal(da, sendAmt));
1792 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1801 using namespace jtx;
1803 Env env(*
this, features);
1809 auto const ammXrpPool = env.
current()->fees().increment * 2;
1826 AMM const ammBob(env,
bob_, ammXrpPool,
USD(150));
1831 Txflags(tfNoRippleDirect | tfPartialPayment));
1834 BEAST_EXPECT(carolUSD >
USD(0) && carolUSD <
USD(50));
1842 using namespace jtx;
1846 Env env(*
this, features);
1858 BEAST_EXPECT(ammBob.expectBalances(
BTC(150),
USD(100), ammBob.tokens()));
1862 Env env(*
this, features);
1875 BEAST_EXPECT(ammBobBtcXrp.expectBalances(
BTC(150),
XRP(100), ammBobBtcXrp.tokens()));
1880 Env env(*
this, features);
1892 BEAST_EXPECT(ammBob.expectBalances(
XRP(150),
USD(100), ammBob.tokens()));
1896 Env env(*
this, features);
1908 BEAST_EXPECT(ammBob.expectBalances(
USD(150),
XRP(100), ammBob.tokens()));
1912 Env env(*
this, features);
1961 Env env(*
this, features);
1994 auto const flowResult = [&] {
2031 BEAST_EXPECT(flowResult.removableOffers.size() == 1);
2033 if (flowResult.removableOffers.empty())
2036 for (
auto const& o : flowResult.removableOffers)
2060 Env env(*
this, features);
2080 Txflags(tfNoRippleDirect | tfPartialPayment));
2083 BEAST_EXPECT(ammBob.expectBalances(
USD(8.4),
XRPAmount{20}, ammBob.tokens()));
2091 using namespace jtx;
2095 Env env(*
this, features);
2106 Txflags(tfNoRippleDirect | tfPartialPayment));
2112 if (!features[fixAMMv1_1])
2115 BEAST_EXPECT(
amm.expectBalances(
2120 BEAST_EXPECT(
amm.expectBalances(
2131 Env env(*
this, features);
2151 Txflags(tfNoRippleDirect | tfPartialPayment));
2164 BEAST_EXPECT(
amm.expectBalances(
2172 Env env(*
this, features);
2185 AMM const amm2(env, ed,
EUR(1'000),
USD(1'000));
2190 Txflags(tfNoRippleDirect | tfPartialPayment));
2194 if (!features[fixAMMv1_1])
2228 Env env(*
this, features);
2239 BEAST_EXPECT(
amm.expectBalances(
USD(1'100),
EUR(1'000),
amm.tokens()));
2247 Env env(*
this, features);
2260 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
2269 BEAST_EXPECT(
amm.expectBalances(
2270 STAmount{GBP, UINT64_C(1'142'857142857143), -12},
USD(875),
amm.tokens()));
2278 Env env(*
this, features);
2291 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
2294 if (!features[fixAMMv1_1])
2301 BEAST_EXPECT(
amm.expectBalances(
GBP(1'024),
USD(1'171.875),
amm.tokens()));
2311 BEAST_EXPECT(
amm.expectBalances(
2312 STAmount{GBP, UINT64_C(1'024'000000000001), -12},
2323 Env env(*
this, features);
2345 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
2348 if (!features[fixAMMv1_1])
2361 STAmount{
GBP, UINT64_C(1'470'421052631579), -12}));
2368 STAmount{
EUR, UINT64_C(929'5789473684212), -13}}}));
2371 BEAST_EXPECT(
amm.expectBalances(
2372 STAmount{EUR, UINT64_C(1'056'336842105263), -12},
2373 STAmount{USD, UINT64_C(1'325'334821428571), -12},
2389 STAmount{
GBP, UINT64_C(1'470'42105263158), -11}));
2396 STAmount{
EUR, UINT64_C(929'57894736842), -11}}}));
2399 BEAST_EXPECT(
amm.expectBalances(
2400 STAmount{EUR, UINT64_C(1'056'336842105264), -12},
2401 STAmount{USD, UINT64_C(1'325'334821428571), -12},
2411 Env env(*
this, features);
2433 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
2436 if (!features[fixAMMv1_1])
2448 BEAST_EXPECT(
amm.expectBalances(
2449 STAmount{GBP, UINT64_C(1'056'336842105263), -12},
2450 STAmount{EUR, UINT64_C(946'6677295918366), -13},
2465 BEAST_EXPECT(
amm.expectBalances(
2466 STAmount{GBP, UINT64_C(1'056'336842105264), -12},
2467 STAmount{EUR, UINT64_C(946'6677295918366), -13},
2476 STAmount{
EUR, UINT64_C(1'442'665816326531), -12}));
2483 STAmount{
USD, UINT64_C(1'340'267857142857), -12}}}));
2491 Env env(*
this, features);
2504 AMM const amm2(env, ed,
EUR(1'000),
USD(1'400));
2511 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
2514 if (!features[fixAMMv1_1])
2523 STAmount{GBP, UINT64_C(1'086'024691358025), -12},
2524 STAmount{EUR, UINT64_C(920'78937795562), -11},
2529 STAmount{EUR, UINT64_C(1'063'368497635504), -12},
2530 STAmount{USD, UINT64_C(1'316'570881226053), -12},
2542 STAmount{GBP, UINT64_C(1'086'024691358027), -12},
2543 STAmount{EUR, UINT64_C(920'7893779556188), -13},
2548 STAmount{EUR, UINT64_C(1'063'368497635505), -12},
2549 STAmount{USD, UINT64_C(1'316'570881226053), -12},
2559 Env env(*
this, features);
2574 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
2577 if (!features[fixAMMv1_1])
2581 STAmount{GBP, UINT64_C(1'108'148148148149), -12},
2582 STAmount{EUR, UINT64_C(902'4064171122988), -13},
2587 STAmount{EUR, UINT64_C(1'078'074866310161), -12},
2588 STAmount{USD, UINT64_C(1'298'611111111111), -12},
2595 STAmount{GBP, UINT64_C(1'108'148148148151), -12},
2596 STAmount{EUR, UINT64_C(902'4064171122975), -13},
2601 STAmount{EUR, UINT64_C(1'078'074866310162), -12},
2602 STAmount{USD, UINT64_C(1'298'611111111111), -12},
2619 using namespace jtx;
2632 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
2634 BEAST_EXPECT(ammBob.expectBalances(
XRP(1'050),
USD(1'000), ammBob.tokens()));
2645 using namespace jtx;
2683 auto const jpy =
gw_[
"JPY"];
2691 AMM const ammAliceXrpJpy(env,
alice_,
XRP(100), jpy(100));
2706 using namespace jtx;
2707 Env env(*
this, features);
2708 auto const dan =
Account(
"dan");
2709 auto const ed =
Account(
"ed");
2720 AMM const ammEd(env, ed,
XRP(9),
USD(11));
2725 if (!features[fixAMMv1_1])
2753 testcase(
"Convert all of an asset using DeliverMin");
2755 using namespace jtx;
2758 Env env(*
this, features);
2792 Env env(*
this, features);
2806 Env env(*
this, features);
2827 auto const dan =
Account(
"dan");
2828 Env env(*
this, features);
2836 AMM const ammDan(env, dan,
XRP(1'000),
USD(1'100));
2837 if (!features[fixAMMv1_1])
2859 STAmount{USD, UINT64_C(999'99999909091), -11},
2870 using namespace jtx;
2876 Env env(*
this, features);
2896 env(
fset(becky, asfDepositAuth));
2912 using namespace jtx;
2933 auto failedIouPayments = [
this, &env]() {
2955 failedIouPayments();
2972 failedIouPayments();
2979 failedIouPayments();
3002 Env env(*
this, features);
3008 env.
fund(
XRP(1'000), g1, alice, bob);
3011 env.
trust(g1[
"USD"](100), bob);
3012 env.
trust(g1[
"USD"](205), alice);
3015 env(
pay(g1, bob, g1[
"USD"](10)));
3016 env(
pay(g1, alice, g1[
"USD"](205)));
3019 AMM const ammAlice(env, alice,
XRP(500), g1[
"USD"](105));
3025 BEAST_EXPECT(
lines[jss::lines][0u][jss::account] == g1.
human());
3026 BEAST_EXPECT(
lines[jss::lines][0u][jss::limit] ==
"100");
3027 BEAST_EXPECT(
lines[jss::lines][0u][jss::balance] ==
"10");
3034 BEAST_EXPECT(
lines[jss::lines][0u][jss::account] == g1.
human());
3035 BEAST_EXPECT(
lines[jss::lines][0u][jss::limit] ==
"205");
3037 BEAST_EXPECT(
lines[jss::lines][0u][jss::balance] ==
"100");
3042 env(
pay(alice, bob, g1[
"USD"](1)));
3045 env(
pay(bob, alice, g1[
"USD"](1)));
3050 env(
trust(g1, bob[
"USD"](0), tfSetFreeze));
3056 env(
offer(bob, g1[
"USD"](5),
XRP(25)));
3066 env(
pay(alice, bob, g1[
"USD"](1)));
3077 for (
auto const& it :
lines[jss::lines])
3079 if (it[jss::account] == bob.
human())
3085 if (!BEAST_EXPECT(bobLine))
3087 BEAST_EXPECT(bobLine[jss::freeze] ==
true);
3088 BEAST_EXPECT(bobLine[jss::balance] ==
"-16");
3095 for (
auto const& it :
lines[jss::lines])
3097 if (it[jss::account] == g1.
human())
3103 if (!BEAST_EXPECT(g1Line))
3105 BEAST_EXPECT(g1Line[jss::freeze_peer] ==
true);
3106 BEAST_EXPECT(g1Line[jss::balance] ==
"16");
3112 env(
trust(g1, bob[
"USD"](0), tfClearFreeze));
3117 auto ff = affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
3119 ff[sfLowLimit.fieldName] ==
3121 BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfLowFreeze));
3122 BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfHighFreeze));
3133 Env env(*
this, features);
3143 env.
fund(
XRP(20'000), a2, a3, a4);
3146 env.
trust(g1[
"USD"](1'200), a1);
3147 env.
trust(g1[
"USD"](200), a2);
3148 env.
trust(g1[
"BTC"](100), a3);
3149 env.
trust(g1[
"BTC"](100), a4);
3152 env(
pay(g1, a1, g1[
"USD"](1'000)));
3153 env(
pay(g1, a2, g1[
"USD"](100)));
3154 env(
pay(g1, a3, g1[
"BTC"](100)));
3155 env(
pay(g1, a4, g1[
"BTC"](100)));
3158 AMM const ammG1(env, g1,
XRP(10'000), g1[
"USD"](100));
3168 "book_offers",
std::string(
"USD/") + g1.
human(),
"XRP")[jss::result][jss::offers];
3180 "book_offers",
"XRP",
std::string(
"USD/") + g1.
human())[jss::result][jss::offers];
3195 AMM ammA3(env, a3, g1[
"BTC"](1),
XRP(1));
3201 env(
pay(g1, a2, g1[
"USD"](1)));
3204 env(
pay(a2, g1, g1[
"USD"](1)));
3207 env(
pay(a2, a1, g1[
"USD"](1)));
3210 env(
pay(a1, a2, g1[
"USD"](1)));
3219 env(
fset(g1, asfGlobalFreeze));
3234 "book_offers",
"XRP",
std::string(
"USD/") + g1.
human())[jss::result][jss::offers];
3239 "book_offers",
std::string(
"USD/") + g1.
human(),
"XRP")[jss::result][jss::offers];
3247 env(
pay(g1, a2, g1[
"USD"](1)));
3250 env(
pay(a2, g1, g1[
"USD"](1)));
3260 testcase(
"Offers for Frozen Trust Lines");
3263 Env env(*
this, features);
3270 env.
fund(
XRP(2'000), g1, a3, a4);
3274 env.
trust(g1[
"USD"](1'000), a2);
3275 env.
trust(g1[
"USD"](2'000), a3);
3276 env.
trust(g1[
"USD"](2'001), a4);
3279 env(
pay(g1, a3, g1[
"USD"](2'000)));
3280 env(
pay(g1, a4, g1[
"USD"](2'001)));
3283 AMM const ammA3(env, a3,
XRP(1'000), g1[
"USD"](1'001));
3293 env(
offer(a4,
XRP(999), g1[
"USD"](999)));
3300 env(
trust(g1, a3am, tfSetFreeze));
3302 BEAST_EXPECT(info[jss::amm][jss::asset2_frozen].asBool());
3313 env(
trust(g1, a4[
"USD"](0), tfSetFreeze));
3317 env(
offer(a2, g1[
"USD"](999),
XRP(999)));
3329 testcase(
"Multisign AMM Transactions");
3331 using namespace jtx;
3332 Env env{*
this, features};
3337 fund(env,
gw_, {alice, becky, zelda},
XRP(20'000), {
USD(20'000)});
3341 env(
regkey(alice, alie));
3342 env(
fset(alice, asfDisableMaster),
Sig(alice));
3345 env(
signers(alice, 2, {{becky, 1}, {bogie, 1}}),
Sig(alie));
3349 Msig const ms{becky, bogie};
3366 ammAlice.
deposit(alice, 1'000'000);
3369 ammAlice.
withdraw(alice, 1'000'000);
3372 ammAlice.
vote({}, 1'000);
3375 env(ammAlice.
bid({.account = alice, .bidMin = 100}), ms).
close();
3386 using namespace jtx;
3390 Env env(*
this, features);
3408 using namespace jtx;
3412 Env env(*
this, features);
3413 auto const bobUsd =
bob_[
"USD"];
3414 auto const bobEur =
bob_[
"EUR"];
3421 AMM const ammBobXrpUsd(env,
bob_,
XRP(100), bobUsd(100));
3424 AMM const ammBobUsdEur(env,
bob_, bobUsd(100), bobEur(100));
3439 Txflags(tfNoRippleDirect | tfPartialPayment),
3444 Env env(*
this, features);
3458 Env env(*
this, features);
3477 using namespace jtx;
3479 auto const cny =
gw_[
"CNY"];
3482 Env env(*
this, features);
3503 Env env(*
this, features);
3517 AMM const ammBobEurCny(env,
bob_,
EUR(100), cny(100));
3543 using namespace jtx;
3556 using namespace jtx;
3564 using namespace jtx;
A generic endpoint for log messages.
TestcaseT testcase
Memberspace for declaring test cases.
Floating point representation of amounts with high dynamic range.
A currency issued by an account.
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.
void pushBack(STPath const &e)
Discardable, editable view to a ledger.
virtual beast::Journal getJournal(std::string const &name)=0
virtual OpenLedger & getOpenLedger()=0
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
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 pathFindConsumeAll()
void testSellFlagExceedLimit(FeatureBitset features)
void testRmFundedOffer(FeatureBitset features)
void viaOffersViaGateway()
void testSelfIssueOffer(FeatureBitset features)
void testStepLimit(FeatureBitset features)
void testSellFlagBasic(FeatureBitset features)
void testTxMultisign(FeatureBitset features)
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 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 testConvertAllOfAnAsset(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)
TestPath & pushBack(Issue const &iss)
jtx::Account const alice_
void testAMM(std::function< void(jtx::AMM &, jtx::Env &)> const &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={testableAmendments()})
testAMM() funds 30,000XRP and 30,000IOU for each non-XRP asset to Alice and Carol
static FeatureBitset testableAmendments()
jtx::Account const carol_
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
static XRPAmount ammCrtFee(jtx::Env &env)
Convenience class to test AMM functionality.
bool expectTradingFee(std::uint16_t fee) const
json::Value bid(BidArg const &arg)
IOUAmount withdrawAll(std::optional< Account > const &account, std::optional< STAmount > const &asset1OutDetails=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 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)
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)
json::Value ammRpcInfo(std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< Asset > const &asset1=std::nullopt, std::optional< Asset > const &asset2=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt, bool ignoreParams=false, unsigned apiVersion=rpc::kApiInvalidVersion) const
Send amm_info RPC command.
AccountID const & ammAccount() 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< Asset, Asset > > const &assets=std::nullopt, std::optional< Ter > const &ter=std::nullopt)
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.
Sets the DeliverMin on a JTx.
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)
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
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.
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.
@ Array
array value (ordered list)
Keylet offer(AccountID const &id, SeqProxy const &seq) noexcept
An offer from an account.
Keylet account(AccountID const &id) noexcept
AccountID root.
void nOffers(Env &env, std::size_t n, Account const &account, STAmount const &in, STAmount const &out)
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::vector< STAmount > fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
bool expectLedgerEntryRoot(Env &env, Account const &acct, STAmount const &expectedValue)
json::Value regkey(Account const &account, DisabledT)
Disable the regular key.
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)
STPathElement allPathElements(AccountID const &a, Asset const &asset)
bool expectHolding(Env &env, AccountID const &account, STAmount const &value, bool defaultLimits)
XrpT const XRP
Converts to XRP Issue or STAmount.
json::Value noop(Account const &account)
The null transaction.
OwnerCount< ltRIPPLE_STATE > lines
Match the number of trust lines in the account's owner directory.
bool same(STPathSet const &st1, Args const &... args)
XRPAmount txFee(Env const &env, std::uint16_t n)
std::tuple< STPathSet, STAmount, STAmount > findPaths(jtx::Env &env, jtx::Account const &src, jtx::Account const &dst, STAmount const &saDstAmount, std::optional< STAmount > const &saSendMax, std::optional< PathAsset > const &srcAsset, std::optional< AccountID > const &srcIssuer, std::optional< uint256 > const &domain)
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
FeatureBitset testableAmendments()
STPathElement cpe(PathAsset const &pa)
STPathElement ipe(Asset const &asset)
json::Value ledgerEntryState(Env &env, Account const &acctA, Account const &acctB, std::string const ¤cy)
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
bool equal(STAmount const &sa1, STAmount const &sa2)
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)
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
OwnerCount< ltOFFER > offers
Match the number of offers in the account's owner directory.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
STPath stpath(Args const &... args)
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
json::Value getAccountLines(Env &env, AccountID const &acctId)
json::Value signers(Account const &account, std::uint32_t quorum, std::vector< Signer > const &v)
bool checkArraySize(json::Value const &val, unsigned int size)
json::Value getAccountOffers(Env &env, AccountID const &acct, bool current)
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
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.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
TAmounts< STAmount, STAmount > Amounts
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
TER offerDelete(ApplyView &view, SLE::ref sle, beast::Journal j)
Delete an offer.
Currency const & xrpCurrency()
XRP currency.
std::string to_string(BaseUInt< Bits, Tag > const &a)
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.
TERSubset< CanCvtToTER > TER
AccountID const & xrpAccount()
Compute AccountID from public key.
Represents an XRP, IOU, or MPT quantity This customizes the string conversion and supports XRP conver...
STAmount const & value() const