1837 using namespace jtx;
1842 auto const baseFee = env.
current()->fees().base.drops();
1887 [&](
AMM& ammAlice,
Env& env) {
1891 BEAST_EXPECT(ammAlice.expectBalances(
1892 XRP(9'000), USD(10'000), IOUAmount{9'486'832'98050514, -8}));
1903 {all, all - fixAMMv1_3});
1906 testAMM([&](AMM& ammAlice, Env&) {
1907 ammAlice.withdraw(
alice, 10'000,
USD(0));
1909 ammAlice.expectBalances(
XRP(10'000),
USD(9980.01),
IOUAmount{9'990'000, 0}));
1913 testAMM([&](AMM& ammAlice, Env& env) {
1914 env(
trust(
carol, STAmount{ammAlice.lptIssue(), 10'000}));
1919 ammAlice.withdrawAll(
alice);
1920 BEAST_EXPECT(!ammAlice.ammExists());
1927 ammCarol.expectBalances(
XRP(10'000),
USD(10'000), IOUAmount{10'000'000, 0}));
1931 testAMM([&](AMM& ammAlice, Env& env) {
1932 ammAlice.deposit(
carol,
USD(1'000));
1933 ammAlice.withdrawAll(
carol,
USD(0));
1935 ammAlice.expectBalances(
XRP(10'000),
USD(10'000), IOUAmount{10'000'000, 0}));
1940 testAMM([&](AMM& ammAlice, Env&) {
1941 ammAlice.deposit(
carol,
USD(1'000));
1942 ammAlice.withdrawAll(
carol,
XRP(0));
1943 BEAST_EXPECT(ammAlice.expectBalances(
1944 XRPAmount(9'090'909'091),
1945 STAmount{
USD, UINT64_C(10'999'99999999999), -11},
1946 IOUAmount{10'000'000, 0}));
1951 [&](AMM& ammAlice, Env& env) {
1955 auto lpTokens = ammAlice.deposit(
carol,
USD(1'000));
1956 ammAlice.withdraw(
carol, lpTokens,
USD(0));
1957 lpTokens = ammAlice.deposit(
carol, STAmount(
USD, 1, -6));
1958 ammAlice.withdraw(
carol, lpTokens,
USD(0));
1959 lpTokens = ammAlice.deposit(
carol, XRPAmount(1));
1960 ammAlice.withdraw(
carol, lpTokens, XRPAmount(0));
1961 if (!env.enabled(fixAMMv1_3))
1964 ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
1968 BEAST_EXPECT(ammAlice.expectBalances(
1969 XRPAmount(10'000'000'001), USD(10'000), ammAlice.tokens()));
1971 BEAST_EXPECT(ammAlice.expectLPTokens(
carol, IOUAmount{0}));
1976 {all, all - fixAMMv1_3});
1980 testAMM([&](AMM& ammAlice, Env&) {
1981 auto const carolTokens = ammAlice.deposit(
carol,
USD(1'000));
1982 auto const aliceTokens = ammAlice.deposit(
alice,
USD(1'000));
1983 ammAlice.withdraw(
alice, aliceTokens,
USD(0));
1984 ammAlice.withdraw(
carol, carolTokens,
USD(0));
1985 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000),
USD(10'000), ammAlice.tokens()));
1986 BEAST_EXPECT(ammAlice.expectLPTokens(
carol, IOUAmount{0}));
1987 BEAST_EXPECT(ammAlice.expectLPTokens(
alice, ammAlice.tokens()));
1991 testAMM([&](AMM& ammAlice, Env&) {
1992 ammAlice.deposit(
carol, 1'000'000);
1993 ammAlice.withdrawAll(
carol);
1995 ammAlice.expectBalances(
XRP(10'000),
USD(10'000), IOUAmount{10'000'000, 0}));
1999 testAMM([&](AMM& ammAlice, Env&) {
2000 ammAlice.deposit(
carol, 1'000'000);
2001 ammAlice.withdrawAll(
carol,
USD(0));
2002 BEAST_EXPECT(ammAlice.expectBalances(
2004 STAmount{
USD, UINT64_C(9'090'909090909092), -12},
2005 IOUAmount{10'000'000, 0}));
2009 testAMM([&](AMM& ammAlice, Env&) {
2010 ammAlice.deposit(
carol, 1'000'000);
2011 ammAlice.withdrawAll(
carol,
XRP(0));
2012 BEAST_EXPECT(ammAlice.expectBalances(
2013 XRPAmount(9'090'909'091),
USD(11'000), IOUAmount{10'000'000, 0}));
2018 [&](AMM& ammAlice, Env& env) {
2019 ammAlice.deposit(
carol, 1'000'000);
2021 BEAST_EXPECT(ammAlice.expectLPTokens(
carol, IOUAmount{153'846'15384616, -8}));
2022 if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
2024 BEAST_EXPECT(ammAlice.expectBalances(
2025 XRPAmount(11'000'000'000),
2026 STAmount{
USD, UINT64_C(9'372'781065088757), -12},
2027 IOUAmount{10'153'846'15384616, -8}));
2029 else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
2031 BEAST_EXPECT(ammAlice.expectBalances(
2032 XRPAmount(11'000'000'000),
2033 STAmount{
USD, UINT64_C(9'372'781065088769), -12},
2034 IOUAmount{10'153'846'15384616, -8}));
2036 else if (env.enabled(fixAMMv1_3))
2038 BEAST_EXPECT(ammAlice.expectBalances(
2039 XRPAmount(11'000'000'000),
2040 STAmount{
USD, UINT64_C(9'372'78106508877), -11},
2041 IOUAmount{10'153'846'15384616, -8}));
2043 ammAlice.withdrawAll(
carol);
2044 BEAST_EXPECT(ammAlice.expectLPTokens(
carol, IOUAmount{0}));
2046 {.features = {all, all - fixAMMv1_3, all - fixAMMv1_1 - fixAMMv1_3}, .noLog =
true});
2050 [&](AMM& ammAlice, Env& env) {
2051 ammAlice.deposit(
carol, 1'000'000);
2053 BEAST_EXPECT(ammAlice.expectLPTokens(
carol, IOUAmount{153'846'15384616, -8}));
2054 if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
2056 BEAST_EXPECT(ammAlice.expectBalances(
2058 STAmount{
USD, UINT64_C(9'372'781065088757), -12},
2059 IOUAmount{10'153'846'15384616, -8}));
2061 else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
2063 BEAST_EXPECT(ammAlice.expectBalances(
2065 STAmount{
USD, UINT64_C(9'372'781065088769), -12},
2066 IOUAmount{10'153'846'15384616, -8}));
2068 else if (env.enabled(fixAMMv1_3))
2070 BEAST_EXPECT(ammAlice.expectBalances(
2072 STAmount{
USD, UINT64_C(9'372'78106508877), -11},
2073 IOUAmount{10'153'846'15384616, -8}));
2079 {all, all - fixAMMv1_3, all - fixAMMv1_1 - fixAMMv1_3});
2089 BEAST_EXPECT(ammAlice.expectBalances(
USD(20'000),
BTC(0.5), IOUAmount{100, 0}));
2094 ammAlice.deposit(
carol, 10);
2095 BEAST_EXPECT(ammAlice.expectBalances(
USD(22'000),
BTC(0.55), IOUAmount{110, 0}));
2099 ammAlice.withdraw(
carol, 10);
2100 BEAST_EXPECT(ammAlice.expectBalances(
USD(20'000),
BTC(0.5), IOUAmount{100, 0}));
2101 BEAST_EXPECT(ammAlice.expectLPTokens(
carol, IOUAmount{0, 0}));
2107 testAMM([&](AMM& ammAlice, Env&) {
2109 ammAlice.withdraw(
alice, IOUAmount{1, -3});
2110 BEAST_EXPECT(ammAlice.expectBalances(
2111 XRPAmount{9'999'999'999},
2112 STAmount{USD, UINT64_C(9'999'999999), -6},
2113 IOUAmount{9'999'999'999, -3}));
2116 [&](AMM& ammAlice, Env& env) {
2119 if (!env.enabled(fixAMMv1_3))
2121 BEAST_EXPECT(ammAlice.expectBalances(
2122 XRPAmount{9'999'999'999},
USD(10'000), IOUAmount{9'999'999'9995, -4}));
2126 BEAST_EXPECT(ammAlice.expectBalances(
2127 XRP(10'000),
USD(10'000), IOUAmount{9'999'999'9995, -4}));
2133 {all, all - fixAMMv1_3});
2134 testAMM([&](AMM& ammAlice, Env&) {
2136 ammAlice.withdraw(alice,
std::nullopt, STAmount{USD, 1, -10});
2137 BEAST_EXPECT(ammAlice.expectBalances(
2139 STAmount{USD, UINT64_C(9'999'9999999999), -10},
2140 IOUAmount{9'999'999'99999995, -8}));
2145 testAMM([&](AMM& ammAlice, Env&) {
2146 ammAlice.withdraw(alice, IOUAmount{9'999'999'999, -3});
2148 ammAlice.expectBalances(XRPAmount{1}, STAmount{USD, 1, -6}, IOUAmount{1, -3}));
2151 testAMM([&](AMM& ammAlice, Env&) {
2152 ammAlice.withdraw(alice, IOUAmount{9'999'999}, USD(0));
2153 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), STAmount{USD, 1, -10}, IOUAmount{1}));
2156 testAMM([&](AMM& ammAlice, Env&) {
2157 ammAlice.withdraw(alice, IOUAmount{9'999'900},
XRP(0));
2158 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, USD(10'000), IOUAmount{100}));
2161 testAMM([&](AMM& ammAlice, Env&) {
2162 ammAlice.withdraw(alice, STAmount{USD, UINT64_C(9'999'99999999999), -11});
2163 BEAST_EXPECT(ammAlice.expectBalances(
2164 XRP(10000), STAmount{USD, 1, -11}, IOUAmount{316227765, -9}));
2167 testAMM([&](AMM& ammAlice, Env&) {
2168 ammAlice.withdraw(alice, XRPAmount{9'999'999'999});
2169 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, USD(10'000), IOUAmount{100}));
2559 using namespace jtx;
2564 features = features - featureSingleAssetVault - featureLendingProtocol;
2570 [&](
AMM& ammAlice,
Env& env) {
2571 ammAlice.
deposit(carol, 1'000'000);
2572 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
2585 [&](
AMM& ammAlice,
Env& env) {
2586 ammAlice.
deposit(carol, 1'000'000);
2588 env(ammAlice.
bid({.account = carol, .bidMin = 110, .bidMax = 110}));
2593 env(ammAlice.
bid({.account = alice, .bidMin = 180, .bidMax = 200}));
2605 [&](
AMM& ammAlice,
Env& env) {
2606 ammAlice.
deposit(carol, 1'000'000);
2608 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
2611 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
2612 ammAlice.
deposit(bob, 1'000'000);
2614 env(ammAlice.
bid({.account = bob}));
2624 env(ammAlice.
bid({.account = carol, .bidMax = 600}));
2636 env(ammAlice.
bid({.account = carol, .bidMin = 100, .bidMax = 600}));
2646 [&](
AMM& ammAlice,
Env& env) {
2647 ammAlice.
deposit(carol, 1'000'000);
2649 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
2650 ammAlice.
deposit(bob, 1'000'000);
2651 if (!features[fixAMMv1_3])
2663 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
2667 env(ammAlice.
bid({.account = bob}));
2672 env(ammAlice.
bid({.account = carol}));
2677 env(ammAlice.
bid({.account = bob}));
2683 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
2686 if (!features[fixAMMv1_3])
2689 XRP(12'000), USD(12'000),
IOUAmount{11'999'678'91, -2}));
2707 [&](
AMM& ammAlice,
Env& env) {
2710 fund(env, gw, {bob, dan, ed}, {USD(20'000)}, Fund::Acct);
2711 ammAlice.
deposit(bob, 1'000'000);
2712 ammAlice.
deposit(ed, 1'000'000);
2713 ammAlice.
deposit(carol, 500'000);
2714 ammAlice.
deposit(dan, 500'000);
2719 .authAccounts = {bob, ed},
2721 auto const slotPrice =
IOUAmount{5'200};
2722 ammTokens -= slotPrice;
2724 if (!features[fixAMMv1_3])
2734 for (
int i = 0; i < 10; ++i)
2736 auto tokens = ammAlice.
deposit(carol, USD(100));
2737 ammAlice.
withdraw(carol, tokens, USD(0));
2738 tokens = ammAlice.
deposit(bob, USD(100));
2739 ammAlice.
withdraw(bob, tokens, USD(0));
2740 tokens = ammAlice.
deposit(ed, USD(100));
2741 ammAlice.
withdraw(ed, tokens, USD(0));
2744 if (!features[fixAMMv1_1])
2748 STAmount(USD, UINT64_C(29'499'00572620545), -11));
2750 env.
balance(bob, USD) ==
STAmount(USD, UINT64_C(18'999'00572616195), -11));
2752 env.
balance(ed, USD) ==
STAmount(USD, UINT64_C(18'999'00572611841), -11));
2755 XRP(13'000),
STAmount(USD, UINT64_C(13'002'98282151419), -11), ammTokens));
2761 STAmount(USD, UINT64_C(29'499'00572620544), -11));
2763 env.
balance(bob, USD) ==
STAmount(USD, UINT64_C(18'999'00572616194), -11));
2765 env.
balance(ed, USD) ==
STAmount(USD, UINT64_C(18'999'0057261184), -10));
2767 if (!features[fixAMMv1_3])
2771 STAmount(USD, UINT64_C(13'002'98282151422), -11),
2778 STAmount(USD, UINT64_C(13'002'98282151422), -11),
2784 for (
int i = 0; i < 10; ++i)
2786 auto const tokens = ammAlice.
deposit(dan, USD(100));
2787 ammAlice.
withdraw(dan, tokens, USD(0));
2792 if (!features[fixAMMv1_1])
2795 env.
balance(dan, USD) ==
STAmount(USD, UINT64_C(19'490'056722744), -9));
2798 XRP(13'000),
STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens));
2800 ammAlice.
deposit(carol, USD(100));
2803 XRP(13'000),
STAmount{USD, UINT64_C(13'112'92609877019), -11}, ammTokens));
2810 STAmount{USD, UINT64_C(13'012'92609877019), -11},
2815 if (!features[fixAMMv1_3])
2819 STAmount(USD, UINT64_C(19'490'05672274399), -11));
2825 STAmount(USD, UINT64_C(19'490'05672274398), -11));
2828 if (!features[fixAMMv1_3])
2832 STAmount{USD, UINT64_C(13'012'92609877023), -11},
2839 STAmount{USD, UINT64_C(13'012'92609877024), -11},
2843 ammAlice.
deposit(carol, USD(100));
2845 if (!features[fixAMMv1_3])
2849 STAmount{USD, UINT64_C(13'112'92609877023), -11},
2856 STAmount{USD, UINT64_C(13'112'92609877024), -11},
2863 if (!features[fixAMMv1_3])
2867 STAmount{USD, UINT64_C(13'012'92609877023), -11},
2874 STAmount{USD, UINT64_C(13'012'92609877024), -11},
2884 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
2888 STAmount{USD, UINT64_C(13'114'03663047264), -11},
2891 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
2895 STAmount{USD, UINT64_C(13'114'03663047269), -11},
2902 STAmount{USD, UINT64_C(13'114'03663044937), -11},
2909 if (!features[fixAMMv1_1])
2913 STAmount(USD, UINT64_C(29'399'00572620545), -11));
2915 else if (!features[fixAMMv1_3])
2919 STAmount(USD, UINT64_C(29'399'00572620544), -11));
2922 for (
int i = 0; i < 10; ++i)
2924 auto const tokens = ammAlice.
deposit(carol, USD(100));
2925 ammAlice.
withdraw(carol, tokens, USD(0));
2929 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
2933 STAmount(USD, UINT64_C(29'389'06197177128), -11));
2936 STAmount{USD, UINT64_C(13'123'98038490681), -11},
2939 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
2943 STAmount(USD, UINT64_C(29'389'06197177124), -11));
2946 STAmount{USD, UINT64_C(13'123'98038490689), -11},
2953 STAmount(USD, UINT64_C(29'389'06197177129), -11));
2956 STAmount{USD, UINT64_C(13'123'98038488352), -11},
2964 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
2968 STAmount{USD, UINT64_C(13'023'98038490681), -11},
2971 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
2975 STAmount{USD, UINT64_C(13'023'98038490689), -11},
2982 STAmount{USD, UINT64_C(13'023'98038488352), -11},
2993 [&](AMM& ammAlice, Env& env) {
2995 auto const tiny =
Number{STAmount::cMinValue, STAmount::cMinOffset};
2996 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{tiny}}));
2999 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny}));
3001 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(10'000), ammAlice.tokens()));
3005 .bidMin = IOUAmount{STAmount::cMinValue, STAmount::cMinOffset},
3008 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny * Number{105, -2}}));
3011 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(10'000), ammAlice.tokens()));
3020 [&](AMM& ammAlice, Env& env) {
3023 .bidMin = IOUAmount{100},
3024 .authAccounts = {carol},
3026 BEAST_EXPECT(ammAlice.expectAuctionSlot({carol}));
3027 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{100}}));
3028 BEAST_EXPECT(ammAlice.expectAuctionSlot({}));
3031 fund(env, {bob, dan},
XRP(1'000));
3034 .bidMin = IOUAmount{100},
3035 .authAccounts = {bob, dan},
3037 BEAST_EXPECT(ammAlice.expectAuctionSlot({bob, dan}));
3046 Env env(*
this, features);
3047 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
3048 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
3049 auto const lpIssue =
amm.lptIssue();
3050 env.trust(STAmount{lpIssue, 500}, alice);
3051 env.trust(STAmount{lpIssue, 50}, bob);
3052 env(
pay(gw, alice, STAmount{lpIssue, 500}));
3053 env(
pay(gw, bob, STAmount{lpIssue, 50}));
3055 env(
amm.bid({.account = alice, .bidMin = 500}));
3056 BEAST_EXPECT(
amm.expectAuctionSlot(100, 0, IOUAmount{500}));
3057 BEAST_EXPECT(
expectHolding(env, alice, STAmount{lpIssue, 0}));
3060 env(
pay(alice, bob, USD(10)), path(~USD), sendmax(
XRP(11)));
3061 BEAST_EXPECT(
amm.expectBalances(
3062 XRPAmount{1'010'010'011}, USD(1'000), IOUAmount{1'004'487'562112089, -9}));
3064 env(
pay(bob, alice,
XRP(10)), path(~XRP), sendmax(USD(11)));
3065 if (!features[fixAMMv1_1])
3067 BEAST_EXPECT(
amm.expectBalances(
3068 XRPAmount{1'000'010'011},
3069 STAmount{USD, UINT64_C(1'010'10090898081), -11},
3070 IOUAmount{1'004'487'562112089, -9}));
3074 BEAST_EXPECT(
amm.expectBalances(
3075 XRPAmount{1'000'010'011},
3076 STAmount{USD, UINT64_C(1'010'100908980811), -12},
3077 IOUAmount{1'004'487'562112089, -9}));
3083 Env env(*
this, features);
3084 auto const baseFee = env.current()->fees().base;
3086 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
3087 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
3088 Json::Value const tx =
amm.bid({.account = alice, .bidMin = 500});
3091 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3092 env.app().config().features.erase(featureAMM);
3093 PreflightContext
const pfCtx(
3094 env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
3095 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
3096 BEAST_EXPECT(pf == temDISABLED);
3097 env.app().config().features.insert(featureAMM);
3101 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3102 jtx.jv[
"TxnSignature"] =
"deadbeef";
3103 jtx.stx = env.ust(jtx);
3104 PreflightContext
const pfCtx(
3105 env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
3106 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
3111 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3112 jtx.jv[
"Asset2"][
"currency"] =
"XRP";
3113 jtx.jv[
"Asset2"].removeMember(
"issuer");
3114 jtx.stx = env.ust(jtx);
3115 PreflightContext
const pfCtx(
3116 env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
3117 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
3118 BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
3272 testcase(
"Basic Payment");
3273 using namespace jtx;
3278 features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol;
3283 [&](
AMM& ammAlice,
Env& env) {
3284 env.
fund(jtx::XRP(30'000), bob);
3286 env(pay(bob, carol, USD(100)),
3298 {{
XRP(10'000), USD(10'100)}},
3305 [&](
AMM& ammAlice,
Env& env) {
3306 env.
fund(jtx::XRP(30'000), bob);
3308 env(pay(bob, carol, USD(100)),
sendmax(
XRP(100)));
3317 {{
XRP(10'000), USD(10'100)}},
3325 [&](
AMM& ammAlice,
Env& env) {
3326 env.
fund(jtx::XRP(30'000), bob);
3337 {{
XRP(10'000), USD(10'100)}},
3344 [&](
AMM& ammAlice,
Env& env) {
3345 env.
fund(jtx::XRP(30'000), bob);
3349 env(pay(bob, carol, USD(100)),
3352 txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
3364 env(pay(bob, carol, USD(100)),
3367 txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality),
3371 {{
XRP(10'000), USD(10'010)}},
3378 [&](
AMM& ammAlice,
Env& env) {
3381 env.
fund(jtx::XRP(30'000), bob);
3386 env(pay(bob, carol, USD(100)),
3389 txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
3398 {{
XRP(10'000), USD(10'010)}},
3405 [&](
AMM& ammAlice,
Env& env) {
3406 env.
fund(jtx::XRP(30'000), bob);
3408 env(pay(bob, carol, USD(100)),
3414 {{
XRP(10'000), USD(10'000)}},
3424 Env env(*
this, features);
3425 fund(env, gw, {alice, carol}, {USD(30'000), EUR(30'000)}, Fund::All);
3429 auto ammEUR_XRP =
AMM(env, alice,
XRP(10'000), EUR(10'000));
3430 auto ammUSD_EUR =
AMM(env, alice, EUR(10'000), USD(10'000));
3431 env(offer(alice,
XRP(101), USD(100)),
txflags(tfPassive));
3433 env(pay(bob, carol, USD(100)),
3438 BEAST_EXPECT(ammEUR_XRP.expectBalances(
3440 STAmount(EUR, UINT64_C(9'970'007498125468), -12),
3441 ammEUR_XRP.tokens()));
3442 if (!features[fixAMMv1_1])
3444 BEAST_EXPECT(ammUSD_EUR.expectBalances(
3445 STAmount(USD, UINT64_C(9'970'097277662122), -12),
3446 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
3447 ammUSD_EUR.tokens()));
3450 Amounts
const expectedAmounts = env.
closed()->rules().enabled(fixReducedOffersV2)
3451 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233787816), -14)}
3455 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
3459 BEAST_EXPECT(ammUSD_EUR.expectBalances(
3460 STAmount(USD, UINT64_C(9'970'097277662172), -12),
3461 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
3462 ammUSD_EUR.tokens()));
3465 Amounts
const expectedAmounts = env.
closed()->rules().enabled(fixReducedOffersV2)
3466 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233782839), -14)}
3470 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
3485 [&](
AMM& ammAlice,
Env& env) {
3488 env.
trust(EUR(2'000), alice);
3490 env(pay(gw, alice, EUR(1'000)));
3491 env(offer(alice,
XRP(101), EUR(100)),
txflags(tfPassive));
3493 env(offer(alice, EUR(100), USD(100)),
txflags(tfPassive));
3495 env(pay(bob, carol, USD(100)),
3502 STAmount(USD, UINT64_C(9'950'01249687578), -11),
3510 STAmount(EUR, UINT64_C(49'98750312422), -11),
3511 STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
3530 [&](
AMM& ammAlice,
Env& env) {
3531 fund(env, gw, {bob}, {USD(100)}, Fund::Acct);
3533 env(offer(bob,
XRP(100), USD(100)),
txflags(tfPassive));
3537 if (!features[fixAMMv1_1])
3548 STAmount(USD, UINT64_C(10'000'00000000001), -11),
3551 env, carol,
STAmount(USD, UINT64_C(30'199'99999999999), -11)));
3558 XRP(30'000) -
XRP(10'000) -
XRP(100) -
XRP(100) - ammCrtFee(env) -
3562 {{
XRP(10'000), USD(10'100)}},
3571 Env env(*
this, features);
3572 fund(env, gw, {alice, bob, carol},
XRP(20'000), {USD(2'000)});
3574 env(offer(bob,
XRP(50), USD(150)),
txflags(tfPassive));
3576 AMM const ammAlice(env, alice,
XRP(1'000), USD(1'050));
3586 [&](
AMM& ammAlice,
Env& env) {
3587 fund(env, gw, {bob}, {USD(1'000)}, Fund::Acct);
3589 env(offer(bob, USD(100),
XRP(100)));
3599 {{
XRP(10'000), USD(10'100)}},
3607 [&](
AMM& ammAlice,
Env& env) {
3608 env(rate(gw, 1.25));
3614 env(offer(carol, EUR(100), GBP(100)));
3624 {{GBP(1'000), EUR(1'100)}},
3630 [&](
AMM& amm,
Env& env) {
3631 env(rate(gw, 1.001));
3633 env(offer(carol,
XRP(100), USD(55)));
3635 if (!features[fixAMMv1_1])
3644 BEAST_EXPECT(amm.expectBalances(
XRP(1'000), USD(500), amm.tokens()));
3645 BEAST_EXPECT(
expectOffers(env, carol, 1, {{Amounts{
XRP(100), USD(55)}}}));
3656 BEAST_EXPECT(amm.expectBalances(
3658 STAmount{USD, UINT64_C(550'000000055), -9},
3669 STAmount(USD, UINT64_C(29'949'94999999494), -11));
3672 {{
XRP(1'000), USD(500)}},
3677 [&](
AMM& amm,
Env& env) {
3678 env(rate(gw, 1.001));
3680 env(offer(carol,
XRP(10), USD(5.5)));
3682 if (!features[fixAMMv1_1])
3684 BEAST_EXPECT(amm.expectBalances(
3685 XRP(990),
STAmount{USD, UINT64_C(505'050505050505), -12}, amm.tokens()));
3690 BEAST_EXPECT(amm.expectBalances(
3691 XRP(990),
STAmount{USD, UINT64_C(505'0505050505051), -13}, amm.tokens()));
3695 {{
XRP(1'000), USD(500)}},
3701 [&](
AMM& ammAlice,
Env& env) {
3703 fund(env, gw, {bob, ed},
XRP(30'000), {GBP(2'000), EUR(2'000)}, Fund::Acct);
3704 env(rate(gw, 1.25));
3712 env(offer(bob, GBP(10),
XRP(10)),
txflags(tfPassive));
3713 env(offer(ed,
XRP(10), EUR(10)),
txflags(tfPassive));
3715 env(offer(carol, EUR(100), GBP(100)));
3717 if (!features[fixAMMv1_1])
3726 STAmount{GBP, UINT64_C(1'037'06583722133), -11},
3727 STAmount{EUR, UINT64_C(1'060'684828792831), -12},
3735 STAmount{EUR, UINT64_C(50'684828792831), -12},
3736 STAmount{GBP, UINT64_C(50'684828792831), -12}}}));
3746 env, carol,
STAmount{GBP, UINT64_C(29'941'16770347333), -11}));
3749 env, carol,
STAmount{EUR, UINT64_C(30'049'31517120716), -11}));
3761 STAmount{GBP, UINT64_C(1'060'684828792832), -12},
3762 STAmount{EUR, UINT64_C(1'037'06583722134), -11},
3770 STAmount{EUR, UINT64_C(27'06583722134028), -14},
3771 STAmount{GBP, UINT64_C(27'06583722134028), -14}}}));
3781 env, carol,
STAmount{GBP, UINT64_C(29'911'64396400896), -11}));
3784 env, carol,
STAmount{EUR, UINT64_C(30'072'93416277865), -11}));
3791 {{GBP(1'000), EUR(1'100)}},
3804 [&](
AMM& ammAlice,
Env& env) {
3805 fund(env, gw, {bob}, {GBP(200), EUR(200)}, Fund::Acct);
3806 env(rate(gw, 1.25));
3808 env(pay(bob, carol, EUR(100)),
3817 {{GBP(1'000), EUR(1'100)}},
3834 [&](
AMM& ammAlice,
Env& env) {
3837 auto const CAN = gw[
"CAN"];
3838 fund(env, gw, {dan}, {CAN(200), GBP(200)}, Fund::Acct);
3839 fund(env, gw, {ed}, {EUR(200), USD(200)}, Fund::Acct);
3840 fund(env, gw, {bob}, {CAN(195.3125)}, Fund::Acct);
3841 env(trust(carol, USD(100)));
3842 env(rate(gw, 1.25));
3844 env(offer(dan, CAN(200), GBP(200)));
3845 env(offer(ed, EUR(200), USD(200)));
3847 env(pay(bob, carol, USD(100)),
3848 path(~GBP, ~EUR, ~USD),
3853 BEAST_EXPECT(
expectHolding(env, dan, CAN(356.25), GBP(43.75)));
3858 {{GBP(10'000), EUR(10'125)}},
3865 [&](
AMM& ammAlice,
Env& env) {
3866 env(pay(alice, carol, USD(99.99)),
3871 env(pay(alice, carol, USD(100)),
3876 env(pay(alice, carol,
XRP(100)),
3887 {{
XRP(100), USD(100)}},
3894 Env env(*
this, features);
3895 auto const ETH = gw[
"ETH"];
3901 {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
3902 fund(env, gw, {carol, bob},
XRP(1'000), {USD(200)}, Fund::Acct);
3903 AMM const xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
3904 AMM const eur_btc(env, alice, EUR(10'000), BTC(10'200));
3905 AMM const btc_usd(env, alice, BTC(10'100), USD(10'000));
3906 AMM const xrp_usd(env, alice,
XRP(10'150), USD(10'200));
3907 AMM const xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
3908 AMM const eth_eur(env, alice, ETH(10'900), EUR(11'000));
3909 AMM const eur_usd(env, alice, EUR(10'100), USD(10'000));
3910 env(pay(bob, carol, USD(100)),
3911 path(~EUR, ~BTC, ~USD),
3913 path(~ETH, ~EUR, ~USD),
3915 if (!features[fixAMMv1_1])
3921 STAmount{ETH, UINT64_C(10'073'65779244494), -11},
3924 STAmount{ETH, UINT64_C(10'926'34220755506), -11},
3925 STAmount{EUR, UINT64_C(10'973'54232078752), -11},
3928 STAmount{EUR, UINT64_C(10'126'45767921248), -11},
3929 STAmount{USD, UINT64_C(9'973'93151712086), -11},
3935 STAmount{USD, UINT64_C(10'126'06848287914), -11},
3942 STAmount{ETH, UINT64_C(10'073'65779244461), -11},
3945 STAmount{ETH, UINT64_C(10'926'34220755539), -11},
3946 STAmount{EUR, UINT64_C(10'973'5423207872), -10},
3949 STAmount{EUR, UINT64_C(10'126'4576792128), -10},
3950 STAmount{USD, UINT64_C(9'973'93151712057), -11},
3956 STAmount{USD, UINT64_C(10'126'06848287943), -11},
3966 BEAST_EXPECT(xrp_eur.expectBalances(
XRP(10'100), EUR(10'000), xrp_eur.tokens()));
3975 Env env(*
this, features);
3976 auto const ETH = gw[
"ETH"];
3982 {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
3983 fund(env, gw, {carol, bob},
XRP(1000), {USD(200)}, Fund::Acct);
3984 AMM const xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
3985 AMM const eur_btc(env, alice, EUR(10'000), BTC(10'200));
3986 AMM const btc_usd(env, alice, BTC(10'100), USD(10'000));
3987 AMM const xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
3988 AMM const eth_eur(env, alice, ETH(10'900), EUR(11'000));
3989 env(pay(bob, carol, USD(100)),
3990 path(~EUR, ~BTC, ~USD),
3991 path(~ETH, ~EUR, ~BTC, ~USD),
3993 if (!features[fixAMMv1_1])
3997 BEAST_EXPECT(xrp_eur.expectBalances(
3999 STAmount{EUR, UINT64_C(9'981'544436337968), -12},
4002 STAmount{EUR, UINT64_C(10'101'16096785173), -11},
4003 STAmount{BTC, UINT64_C(10'097'91426968066), -11},
4006 STAmount{BTC, UINT64_C(10'202'08573031934), -11},
4011 STAmount{ETH, UINT64_C(10'017'41072778012), -11},
4014 STAmount{ETH, UINT64_C(10'982'58927221988), -11},
4015 STAmount{EUR, UINT64_C(10'917'2945958103), -10},
4020 BEAST_EXPECT(xrp_eur.expectBalances(
4022 STAmount{EUR, UINT64_C(9'981'544436337923), -12},
4025 STAmount{EUR, UINT64_C(10'101'16096785188), -11},
4026 STAmount{BTC, UINT64_C(10'097'91426968059), -11},
4029 STAmount{BTC, UINT64_C(10'202'08573031941), -11},
4034 STAmount{ETH, UINT64_C(10'017'41072777996), -11},
4037 STAmount{ETH, UINT64_C(10'982'58927222004), -11},
4038 STAmount{EUR, UINT64_C(10'917'2945958102), -10},
4047 [&](
AMM& ammAlice,
Env& env) {
4049 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
4050 env(trust(alice, EUR(200)));
4051 for (
int i = 0; i < 30; ++i)
4052 env(offer(alice, EUR(1.0 + (0.01 * i)),
XRP(1)));
4055 env(offer(alice, EUR(140),
XRP(100)));
4056 env(pay(bob, carol, USD(100)),
4059 txflags(tfPartialPayment | tfNoRippleDirect));
4060 if (!features[fixAMMv1_1])
4065 STAmount{USD, UINT64_C(9'970'089730807577), -12},
4068 env, carol,
STAmount{USD, UINT64_C(30'029'91026919241), -11}));
4074 STAmount{USD, UINT64_C(9'970'089730807827), -12},
4077 env, carol,
STAmount{USD, UINT64_C(30'029'91026919217), -11}));
4087 [&](
AMM& ammAlice,
Env& env) {
4089 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
4090 env(trust(alice, EUR(200)));
4091 for (
int i = 0; i < 29; ++i)
4092 env(offer(alice, EUR(1.0 + (0.01 * i)),
XRP(1)));
4095 env(offer(alice, EUR(140),
XRP(100)));
4096 env(pay(bob, carol, USD(100)),
4099 txflags(tfPartialPayment | tfNoRippleDirect));
4102 if (!features[fixAMMv1_1])
4106 env, carol,
STAmount{USD, UINT64_C(30'099'99999999999), -11}));
4126 Env env(*
this, features);
4127 fund(env, gw, {alice, carol, bob},
XRP(30'000), {USD(30'000)});
4128 env(offer(bob,
XRP(100), USD(100.001)));
4129 AMM const ammAlice(env, alice,
XRP(10'000), USD(10'100));
4130 env(offer(carol, USD(100),
XRP(100)));
4131 if (!features[fixAMMv1_1])
4135 STAmount{USD, UINT64_C(10'049'92586949302), -11},
4147 STAmount{USD, UINT64_C(10'049'92587049303), -11},
4160 [&](
AMM& ammAlice,
Env& env) {
4161 env(trust(gw, carol[
"USD"](0), tfSetFreeze));
4162 env(trust(gw, alice[
"USD"](0), tfSetFreeze));
4164 env(pay(alice, carol, USD(1)),
4167 txflags(tfNoRippleDirect | tfPartialPayment),
4444 testcase(
"Trading Fee");
4445 using namespace jtx;
4449 [&](
AMM& ammAlice,
Env& env) {
4451 ammAlice.
deposit(carol, USD(3'000));
4457 ammAlice.
vote(alice, 1'000);
4461 ammAlice.
deposit(carol, USD(3'000));
4465 ammAlice.
vote(alice, 0);
4471 {{USD(1'000), EUR(1'000)}},
4479 [&](
AMM& ammAlice,
Env& env) {
4485 ammAlice.
vote(alice, 0);
4487 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
4490 BEAST_EXPECT(tokensFee ==
IOUAmount(485'636'0611129, -7));
4491 BEAST_EXPECT(tokensNoFee ==
IOUAmount(487'644'85901109, -8));
4501 [&](
AMM& ammAlice,
Env& env) {
4503 auto const tokensFee =
4507 ammAlice.
vote(alice, 0);
4509 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
4512 BEAST_EXPECT(tokensFee ==
IOUAmount(98'000'00000002, -8));
4513 BEAST_EXPECT(tokensNoFee ==
IOUAmount(98'475'81871545, -8));
4522 [&](
AMM& ammAlice,
Env& env) {
4524 ammAlice.
deposit(carol, USD(3'000));
4529 ammAlice.
vote(alice, 1'000);
4536 {{USD(1'000), EUR(1'000)}},
4543 [&](
AMM& ammAlice,
Env& env) {
4544 ammAlice.
deposit(carol, 1'000'000);
4545 auto const tokensFee =
4548 auto const balanceAfterWithdraw = [&]() {
4549 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4551 return STAmount(USD, UINT64_C(30'443'43891402715), -11);
4553 if (features[fixAMMv1_1] && !features[fixAMMv1_3])
4555 return STAmount(USD, UINT64_C(30'443'43891402714), -11);
4558 return STAmount(USD, UINT64_C(30'443'43891402713), -11);
4560 BEAST_EXPECT(env.
balance(carol, USD) == balanceAfterWithdraw);
4562 auto const deposit = balanceAfterWithdraw - USD(29'000);
4563 ammAlice.
deposit(carol, deposit);
4565 ammAlice.
vote(alice, 0);
4567 auto const tokensNoFee = ammAlice.
withdraw(carol, deposit);
4568 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4572 STAmount(USD, UINT64_C(30'443'43891402717), -11));
4574 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
4578 STAmount(USD, UINT64_C(30'443'43891402716), -11));
4584 STAmount(USD, UINT64_C(30'443'43891402713), -11));
4588 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4590 BEAST_EXPECT(tokensNoFee ==
IOUAmount(746'579'80779913, -8));
4592 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
4594 BEAST_EXPECT(tokensNoFee ==
IOUAmount(746'579'80779912, -8));
4598 BEAST_EXPECT(tokensNoFee ==
IOUAmount(746'579'80779911, -8));
4600 BEAST_EXPECT(tokensFee ==
IOUAmount(750'588'23529411, -8));
4609 [&](
AMM& ammAlice,
Env& env) {
4610 fund(env, gw, {bob},
XRP(1'000), {USD(1'000), EUR(1'000)}, Fund::Acct);
4616 env(pay(carol, alice, EUR(10)),
4627 ammAlice.
vote(alice, 1'000);
4630 env(pay(bob, carol, USD(10)),
4642 STAmount{EUR, UINT64_C(1'010'10101010101), -11},
4645 {{USD(1'000), EUR(1'010)}},
4652 [&](
AMM& ammAlice,
Env& env) {
4654 env(offer(carol, EUR(10), USD(10)));
4659 env(offer(carol, USD(10), EUR(10)));
4662 ammAlice.
vote(alice, 500);
4664 env(offer(carol, EUR(10), USD(10)));
4677 STAmount{EUR, UINT64_C(5'025125628140703), -15},
4678 STAmount{USD, UINT64_C(5'025125628140703), -15}}}}));
4679 if (!features[fixAMMv1_1])
4682 STAmount{USD, UINT64_C(1'004'974874371859), -12},
4683 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
4689 STAmount{USD, UINT64_C(1'004'97487437186), -11},
4690 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
4694 {{USD(1'000), EUR(1'010)}},
4704 Env env(*
this, features);
4706 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4707 env(offer(carol, EUR(5), USD(5)));
4708 AMM const ammAlice(env, alice, USD(1'005), EUR(1'000));
4711 if (!features[fixAMMv1_1])
4722 STAmount(EUR, UINT64_C(1005'000000000001), -12),
4731 Env env(*
this, features);
4733 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4734 env(offer(carol, EUR(5), USD(5)));
4736 AMM const ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 250);
4739 if (!features[fixAMMv1_1])
4745 STAmount{EUR, UINT64_C(1'005'012546992382), -12},
4754 STAmount{EUR, UINT64_C(1'005'012546992372), -12},
4764 Env env(*
this, features);
4766 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4767 env(offer(carol, EUR(10), USD(10)));
4769 AMM const ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
4782 Env env(*
this, features);
4784 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4785 env(offer(carol, EUR(9), USD(9)));
4787 AMM const ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
4792 USD(1'004),
STAmount{EUR, UINT64_C(1'001'006076703288), -12}, ammAlice.
tokens()));
5183 testcase(
"Offer/Strand Selection");
5184 using namespace jtx;
5187 auto const ETH = gw1[
"ETH"];
5188 auto const CAN = gw1[
"CAN"];
5194 auto prep = [&](
Env& env,
auto gwRate,
auto gw1Rate) {
5195 fund(env, gw, {alice, carol, bob, ed},
XRP(2'000), {USD(2'000)});
5197 fund(env, gw1, {alice, carol, bob, ed}, {ETH(2'000), CAN(2'000)}, Fund::IOUOnly);
5198 env(rate(gw, gwRate));
5199 env(rate(gw1, gw1Rate));
5217 for (
auto i = 0; i < 3; ++i)
5219 Env env(*
this, features);
5220 prep(env, rates.first, rates.second);
5222 if (i == 0 || i == 2)
5224 env(offer(ed, ETH(400), USD(400)),
txflags(tfPassive));
5228 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5229 env(pay(carol, bob, USD(100)),
path(~USD),
sendmax(ETH(500)));
5234 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
5239 ETH(2'000) - env.
balance(carol, ETH),
5240 env.
balance(bob, USD) - USD(2'000)});
5243 BEAST_EXPECT(q[0] > q[1]);
5245 BEAST_EXPECT(q[0] == q[2]);
5252 for (
auto i = 0; i < 3; ++i)
5254 Env env(*
this, features);
5255 prep(env, rates.first, rates.second);
5257 if (i == 0 || i == 2)
5259 env(offer(ed, ETH(400), USD(400)),
txflags(tfPassive));
5263 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5264 env(offer(alice, USD(400), ETH(400)));
5269 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
5271 if (i == 0 || i == 2)
5279 BEAST_EXPECT(
expectOffers(env, alice, 1, {Amounts{USD(400), ETH(400)}}));
5290 for (
auto i = 0; i < 3; ++i)
5292 Env env(*
this, features);
5293 prep(env, rates.first, rates.second);
5295 if (i == 0 || i == 2)
5297 env(offer(ed, ETH(400), USD(300)),
txflags(tfPassive));
5301 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5302 env(pay(carol, bob, USD(100)),
path(~USD),
sendmax(ETH(500)));
5307 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
5309 if (i == 2 && !features[fixAMMv1_1])
5311 if (rates.first == 1.5)
5313 if (!features[fixAMMv1_1])
5320 STAmount{ETH, UINT64_C(378'6327949540823), -13},
5321 STAmount{USD, UINT64_C(283'9745962155617), -13}}}}));
5330 STAmount{ETH, UINT64_C(378'6327949540813), -13},
5331 STAmount{USD, UINT64_C(283'974596215561), -12}}}}));
5336 if (!features[fixAMMv1_1])
5343 STAmount{ETH, UINT64_C(325'299461620749), -12},
5344 STAmount{USD, UINT64_C(243'9745962155617), -13}}}}));
5353 STAmount{ETH, UINT64_C(325'299461620748), -12},
5354 STAmount{USD, UINT64_C(243'974596215561), -12}}}}));
5360 if (rates.first == 1.5)
5367 STAmount{ETH, UINT64_C(378'6327949540812), -13},
5368 STAmount{USD, UINT64_C(283'9745962155609), -13}}}}));
5377 STAmount{ETH, UINT64_C(325'2994616207479), -13},
5378 STAmount{USD, UINT64_C(243'9745962155609), -13}}}}));
5384 ETH(2'000) - env.
balance(carol, ETH),
5385 env.
balance(bob, USD) - USD(2'000)});
5388 BEAST_EXPECT(q[1] > q[0]);
5390 BEAST_EXPECT(q[2] > q[1]);
5394 for (
auto i = 0; i < 3; ++i)
5396 Env env(*
this, features);
5397 prep(env, rates.first, rates.second);
5399 if (i == 0 || i == 2)
5401 env(offer(ed, ETH(400), USD(250)),
txflags(tfPassive));
5405 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5406 env(offer(alice, USD(250), ETH(400)));
5411 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
5417 if (rates.first == 1.5)
5419 if (!features[fixAMMv1_1])
5421 BEAST_EXPECT(
expectOffers(env, ed, 1, {{Amounts{ETH(400), USD(250)}}}));
5427 STAmount{USD, UINT64_C(40'5694150420947), -13},
5428 STAmount{ETH, UINT64_C(64'91106406735152), -14},
5441 STAmount{ETH, UINT64_C(335'0889359326475), -13},
5442 STAmount{USD, UINT64_C(209'4305849579047), -13},
5449 if (!features[fixAMMv1_1])
5457 STAmount{ETH, UINT64_C(335'0889359326485), -13},
5458 STAmount{USD, UINT64_C(209'4305849579053), -13},
5470 STAmount{ETH, UINT64_C(335'0889359326475), -13},
5471 STAmount{USD, UINT64_C(209'4305849579047), -13},
5497 for (
auto i = 0; i < 3; ++i)
5499 Env env(*
this, features);
5500 prep(env, rates.first, rates.second);
5503 if (i == 0 || i == 2)
5505 env(offer(ed, ETH(400), CAN(400)),
txflags(tfPassive));
5506 env(offer(ed, CAN(400), USD(400))),
txflags(tfPassive);
5511 amm.emplace(env, ed, ETH(1'000), USD(1'000));
5513 env(pay(carol, bob, USD(100)),
path(~USD),
path(~CAN, ~USD),
sendmax(ETH(600)));
5518 if (i == 2 && !features[fixAMMv1_1])
5520 if (rates.first == 1.5)
5523 BEAST_EXPECT(amm->expectBalances(
5524 STAmount{ETH, UINT64_C(1'176'66038955758), -11},
5530 BEAST_EXPECT(amm->expectBalances(
5531 STAmount{ETH, UINT64_C(1'179'540094339627), -12},
5532 STAmount{USD, UINT64_C(847'7880529867501), -13},
5539 STAmount{ETH, UINT64_C(343'3179205198749), -13},
5540 STAmount{CAN, UINT64_C(343'3179205198749), -13},
5543 STAmount{CAN, UINT64_C(362'2119470132499), -13},
5544 STAmount{USD, UINT64_C(362'2119470132499), -13},
5550 if (rates.first == 1.5)
5553 BEAST_EXPECT(amm->expectBalances(
5554 STAmount{ETH, UINT64_C(1'176'660389557593), -12},
5560 BEAST_EXPECT(amm->expectBalances(
5561 STAmount{ETH, UINT64_C(1'179'54009433964), -11},
5562 STAmount{USD, UINT64_C(847'7880529867501), -13},
5569 STAmount{ETH, UINT64_C(343'3179205198749), -13},
5570 STAmount{CAN, UINT64_C(343'3179205198749), -13},
5573 STAmount{CAN, UINT64_C(362'2119470132499), -13},
5574 STAmount{USD, UINT64_C(362'2119470132499), -13},
5580 ETH(2'000) - env.
balance(carol, ETH),
5581 env.
balance(bob, USD) - USD(2'000)});
5583 BEAST_EXPECT(q[1] > q[0]);
5584 BEAST_EXPECT(q[2] > q[0] && q[2] < q[1]);
5657 testcase(
"Fix changeSpotPriceQuality");
5658 using namespace jtx;
5663 SucceedShouldSucceedResize,
5680 {
"0.001519763260828713",
"1558701", Quality{5414253689393440221}, 1000, FailShouldSucceed},
5681 {
"0.01099814367603737",
"1892611", Quality{5482264816516900274}, 1000, FailShouldSucceed},
5682 {
"0.78",
"796599", Quality{5630392334958379008}, 1000, FailShouldSucceed},
5683 {
"105439.2955578965",
"49398693", Quality{5910869983721805038}, 400, FailShouldSucceed},
5684 {
"12408293.23445213",
"4340810521", Quality{5911611095910090752}, 997, FailShouldSucceed},
5685 {
"1892611",
"0.01099814367603737", Quality{6703103457950430139}, 1000, FailShouldSucceed},
5686 {
"423028.8508101858",
"3392804520", Quality{5837920340654162816}, 600, FailShouldSucceed},
5687 {
"44565388.41001027",
"73890647", Quality{6058976634606450001}, 1000, FailShouldSucceed},
5688 {
"66831.68494832662",
"16", Quality{6346111134641742975}, 0, FailShouldSucceed},
5689 {
"675.9287302203422",
"1242632304", Quality{5625960929244093294}, 300, FailShouldSucceed},
5690 {
"7047.112186735699",
"1649845866", Quality{5696855348026306945}, 504, FailShouldSucceed},
5691 {
"840236.4402981238",
"47419053", Quality{5982561601648018688}, 499, FailShouldSucceed},
5692 {
"992715.618909774",
"189445631733", Quality{5697835648288106944}, 815, SucceedShouldSucceedResize},
5693 {
"504636667521",
"185545883.9506651", Quality{6343802275337659280}, 503, SucceedShouldSucceedResize},
5694 {
"992706.7218636649",
"189447316000", Quality{5697835648288106944}, 797, SucceedShouldSucceedResize},
5695 {
"1.068737911388205",
"127860278877", Quality{5268604356368739396}, 293, SucceedShouldSucceedResize},
5696 {
"17932506.56880419",
"189308.6043676173", Quality{6206460598195440068}, 311, SucceedShouldSucceedResize},
5697 {
"1.066379294658174",
"128042251493", Quality{5268559341368739328}, 270, SucceedShouldSucceedResize},
5698 {
"350131413924",
"1576879.110907892", Quality{6487411636539049449}, 650,
Fail},
5699 {
"422093460",
"2.731797662057464", Quality{6702911108534394924}, 1000,
Fail},
5700 {
"76128132223",
"367172.7148422662", Quality{6487263463413514240}, 548,
Fail},
5701 {
"132701839250",
"280703770.7695443", Quality{6273750681188885075}, 562,
Fail},
5702 {
"994165.7604612011",
"189551302411", Quality{5697835592690668727}, 815,
Fail},
5703 {
"45053.33303227917",
"86612695359", Quality{5625695218943638190}, 500,
Fail},
5704 {
"199649.077043865",
"14017933007", Quality{5766034667318524880}, 324,
Fail},
5705 {
"27751824831.70903",
"78896950", Quality{6272538159621630432}, 500,
Fail},
5706 {
"225.3731275781907",
"156431793648", Quality{5477818047604078924}, 989,
Fail},
5707 {
"199649.077043865",
"14017933007", Quality{5766036094462806309}, 324,
Fail},
5708 {
"3.590272027140361",
"20677643641", Quality{5406056147042156356}, 808,
Fail},
5709 {
"1.070884664490231",
"127604712776", Quality{5268620608623825741}, 293,
Fail},
5710 {
"3272.448829820197",
"6275124076", Quality{5625710328924117902}, 81,
Fail},
5711 {
"0.009059512633902926",
"7994028", Quality{5477511954775533172}, 1000,
Fail},
5712 {
"1",
"1.0", Quality{0}, 100,
Fail},
5713 {
"1.0",
"1", Quality{0}, 100,
Fail},
5714 {
"10",
"10.0", Quality{xrpIouAmounts10_100}, 100,
Fail},
5715 {
"10.0",
"10", Quality{iouXrpAmounts10_100}, 100,
Fail},
5716 {
"69864389131",
"287631.4543025075", Quality{6487623473313516078}, 451, Succeed},
5717 {
"4328342973",
"12453825.99247381", Quality{6272522264364865181}, 997, Succeed},
5718 {
"32347017",
"7003.93031579449", Quality{6347261126087916670}, 1000, Succeed},
5719 {
"61697206161",
"36631.4583206413", Quality{6558965195382476659}, 500, Succeed},
5720 {
"1654524979",
"7028.659825511603", Quality{6487551345110052981}, 504, Succeed},
5721 {
"88621.22277293179",
"5128418948", Quality{5766347291552869205}, 380, Succeed},
5722 {
"1892611",
"0.01099814367603737", Quality{6703102780512015436}, 1000, Succeed},
5723 {
"4542.639373338766",
"24554809", Quality{5838994982188783710}, 0, Succeed},
5724 {
"5132932546",
"88542.99750172683", Quality{6419203342950054537}, 380, Succeed},
5725 {
"78929964.1549083",
"1506494795", Quality{5986890029845558688}, 589, Succeed},
5726 {
"10096561906",
"44727.72453735605", Quality{6487455290284644551}, 250, Succeed},
5727 {
"5092.219565514988",
"8768257694", Quality{5626349534958379008}, 503, Succeed},
5728 {
"1819778294",
"8305.084302902864", Quality{6487429398998540860}, 415, Succeed},
5729 {
"6970462.633911943",
"57359281", Quality{6054087899185946624}, 850, Succeed},
5730 {
"3983448845",
"2347.543644281467", Quality{6558965195382476659}, 856, Succeed},
5734 {
"771493171",
"1.243473020567508", Quality{6707566798038544272}, 100, SucceedShouldFail},
5738 boost::regex
const rx(
"^\\d+$");
5739 boost::smatch match;
5743 auto rules = env.
current()->rules();
5747 for (
auto const& t : tests)
5757 auto const poolInIsXRP = boost::regex_search(
std::get<0>(t), match, rx);
5758 auto const poolOutIsXRP = boost::regex_search(
std::get<1>(t), match, rx);
5759 assert(!(poolInIsXRP && poolOutIsXRP));
5760 auto const poolIn = getPool(
std::get<0>(t), poolInIsXRP);
5761 auto const poolOut = getPool(
std::get<1>(t), poolOutIsXRP);
5765 Amounts{poolIn, poolOut}, quality, tfee, env.
current()->rules(), env.
journal);
5768 if (status == SucceedShouldSucceedResize)
5770 if (!features[fixAMMv1_1])
5772 BEAST_EXPECT(Quality{*amounts} < quality);
5776 BEAST_EXPECT(Quality{*amounts} >= quality);
5779 else if (status == Succeed)
5781 if (!features[fixAMMv1_1])
5784 Quality{*amounts} >= quality ||
5789 BEAST_EXPECT(Quality{*amounts} >= quality);
5792 else if (status == FailShouldSucceed)
5794 BEAST_EXPECT(features[fixAMMv1_1] && Quality{*amounts} >= quality);
5796 else if (status == SucceedShouldFail)
5799 !features[fixAMMv1_1] && Quality{*amounts} < quality &&
5809 if (status ==
Fail && quality != Quality{0})
5811 auto tinyOffer = [&]() {
5817 swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
5823 swapAssetOut(Amounts{poolIn, poolOut}, takerGets, tfee),
5826 auto const takerPays =
5829 takerPays,
swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
5831 BEAST_EXPECT(Quality(tinyOffer) < quality);
5833 else if (status == FailShouldSucceed)
5835 BEAST_EXPECT(!features[fixAMMv1_1]);
5837 else if (status == SucceedShouldFail)
5839 BEAST_EXPECT(features[fixAMMv1_1]);
5845 BEAST_EXPECT(!strcmp(e.
what(),
"changeSpotPriceQuality failed"));
5846 BEAST_EXPECT(!features[fixAMMv1_1] && status == FailShouldSucceed);
5854 BEAST_EXPECT(!res.has_value());
5920 using namespace jtx;
5926 Account const gatehub{
"gatehub"};
5927 Account const bitstamp{
"bitstamp"};
5928 Account const trader{
"trader"};
5929 auto const usdGH = gatehub[
"USD"];
5930 auto const btcGH = gatehub[
"BTC"];
5931 auto const usdBIT = bitstamp[
"USD"];
5935 char const* testCase;
5936 double const poolUsdBIT;
5937 double const poolUsdGH;
5950 double const offer1BtcGH = 0.1;
5951 double const offer2BtcGH = 0.1;
5952 double const offer2UsdGH = 1;
5953 double const rateBIT = 0.0;
5954 double const rateGH = 0.0;
5959 for (
auto const& input : {
5961 .testCase =
"Test Fix Overflow Offer",
5964 .sendMaxUsdBIT{usdBIT(50)},
5965 .sendUsdGH{usdGH,
uint64_t(272'455089820359), -12},
5968 .failUsdBIT{usdBIT,
uint64_t(46'47826086956522), -14},
5969 .failUsdBITr{usdBIT,
uint64_t(46'47826086956521), -14},
5970 .goodUsdGH{usdGH,
uint64_t(96'7543114220382), -13},
5971 .goodUsdGHr{usdGH,
uint64_t(96'7543114222965), -13},
5972 .goodUsdBIT{usdBIT,
uint64_t(8'464739069120721), -15},
5973 .goodUsdBITr{usdBIT,
uint64_t(8'464739069098152), -15},
5974 .lpTokenBalance = {28'61817604250837, -14},
5975 .lpTokenBalanceAlt =
IOUAmount{28'61817604250836, -14},
5983 .testCase =
"Overflow test {1, 100, 0.111}",
5986 .sendMaxUsdBIT{usdBIT(0.111)},
5987 .sendUsdGH{usdGH, 100},
5990 .failUsdBIT{usdBIT,
uint64_t(1'111), -3},
5991 .failUsdBITr{usdBIT,
uint64_t(1'111), -3},
5992 .goodUsdGH{usdGH,
uint64_t(90'04347888284115), -14},
5993 .goodUsdGHr{usdGH,
uint64_t(90'04347888284201), -14},
5994 .goodUsdBIT{usdBIT,
uint64_t(1'111), -3},
5995 .goodUsdBITr{usdBIT,
uint64_t(1'111), -3},
5996 .lpTokenBalance{10, 0},
5997 .offer1BtcGH = 1e-5,
5999 .offer2UsdGH = 1e-5,
6004 .testCase =
"Overflow test {1, 100, 1.00}",
6007 .sendMaxUsdBIT{usdBIT(1.00)},
6008 .sendUsdGH{usdGH, 100},
6011 .failUsdBIT{usdBIT,
uint64_t(2), 0},
6012 .failUsdBITr{usdBIT,
uint64_t(2), 0},
6013 .goodUsdGH{usdGH,
uint64_t(52'94379354424079), -14},
6014 .goodUsdGHr{usdGH,
uint64_t(52'94379354424135), -14},
6015 .goodUsdBIT{usdBIT,
uint64_t(2), 0},
6016 .goodUsdBITr{usdBIT,
uint64_t(2), 0},
6017 .lpTokenBalance{10, 0},
6018 .offer1BtcGH = 1e-5,
6020 .offer2UsdGH = 1e-5,
6025 .testCase =
"Overflow test {1, 100, 4.6432}",
6028 .sendMaxUsdBIT{usdBIT(4.6432)},
6029 .sendUsdGH{usdGH, 100},
6032 .failUsdBIT{usdBIT,
uint64_t(5'6432), -4},
6033 .failUsdBITr{usdBIT,
uint64_t(5'6432), -4},
6034 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6035 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6036 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
6037 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
6038 .lpTokenBalance{10, 0},
6039 .offer1BtcGH = 1e-5,
6041 .offer2UsdGH = 1e-5,
6046 .testCase =
"Overflow test {1, 100, 10}",
6049 .sendMaxUsdBIT{usdBIT(10)},
6050 .sendUsdGH{usdGH, 100},
6053 .failUsdBIT{usdBIT,
uint64_t(11), 0},
6054 .failUsdBITr{usdBIT,
uint64_t(11), 0},
6055 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6056 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6057 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
6058 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
6059 .lpTokenBalance{10, 0},
6060 .offer1BtcGH = 1e-5,
6062 .offer2UsdGH = 1e-5,
6067 .testCase =
"Overflow test {50, 100, 5.55}",
6070 .sendMaxUsdBIT{usdBIT(5.55)},
6071 .sendUsdGH{usdGH, 100},
6074 .failUsdBIT{usdBIT,
uint64_t(55'55), -2},
6075 .failUsdBITr{usdBIT,
uint64_t(55'55), -2},
6076 .goodUsdGH{usdGH,
uint64_t(90'04347888284113), -14},
6077 .goodUsdGHr{usdGH,
uint64_t(90'0434788828413), -13},
6078 .goodUsdBIT{usdBIT,
uint64_t(55'55), -2},
6079 .goodUsdBITr{usdBIT,
uint64_t(55'55), -2},
6080 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
6081 .offer1BtcGH = 1e-5,
6083 .offer2UsdGH = 1e-5,
6088 .testCase =
"Overflow test {50, 100, 50.00}",
6091 .sendMaxUsdBIT{usdBIT(50.00)},
6092 .sendUsdGH{usdGH, 100},
6093 .failUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
6094 .failUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
6095 .failUsdBIT{usdBIT,
uint64_t(100), 0},
6096 .failUsdBITr{usdBIT,
uint64_t(100), 0},
6097 .goodUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
6098 .goodUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
6099 .goodUsdBIT{usdBIT,
uint64_t(100), 0},
6100 .goodUsdBITr{usdBIT,
uint64_t(100), 0},
6101 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
6102 .offer1BtcGH = 1e-5,
6104 .offer2UsdGH = 1e-5,
6109 .testCase =
"Overflow test {50, 100, 232.16}",
6112 .sendMaxUsdBIT{usdBIT(232.16)},
6113 .sendUsdGH{usdGH, 100},
6116 .failUsdBIT{usdBIT,
uint64_t(282'16), -2},
6117 .failUsdBITr{usdBIT,
uint64_t(282'16), -2},
6118 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6119 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6120 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
6121 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
6122 .lpTokenBalance{70'71067811865475, -14},
6123 .offer1BtcGH = 1e-5,
6125 .offer2UsdGH = 1e-5,
6130 .testCase =
"Overflow test {50, 100, 500}",
6133 .sendMaxUsdBIT{usdBIT(500)},
6134 .sendUsdGH{usdGH, 100},
6137 .failUsdBIT{usdBIT,
uint64_t(550), 0},
6138 .failUsdBITr{usdBIT,
uint64_t(550), 0},
6139 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6140 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6141 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
6142 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
6143 .lpTokenBalance{70'71067811865475, -14},
6144 .offer1BtcGH = 1e-5,
6146 .offer2UsdGH = 1e-5,
6152 testcase(input.testCase);
6153 for (
auto const& features : {all - fixAMMOverflowOffer - fixAMMv1_1 - fixAMMv1_3, all})
6157 env.
fund(
XRP(5'000), gatehub, bitstamp, trader);
6160 if (input.rateGH != 0.0)
6161 env(rate(gatehub, input.rateGH));
6162 if (input.rateBIT != 0.0)
6163 env(rate(bitstamp, input.rateBIT));
6165 env(trust(trader, usdGH(10'000'000)));
6166 env(trust(trader, usdBIT(10'000'000)));
6167 env(trust(trader, btcGH(10'000'000)));
6170 env(pay(gatehub, trader, usdGH(100'000)));
6171 env(pay(gatehub, trader, btcGH(100'000)));
6172 env(pay(bitstamp, trader, usdBIT(100'000)));
6175 AMM const amm{env, trader, usdGH(input.poolUsdGH), usdBIT(input.poolUsdBIT)};
6178 IOUAmount const preSwapLPTokenBalance = amm.getLPTokensBalance();
6180 env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
6181 env(offer(trader, btcGH(input.offer2BtcGH), usdGH(input.offer2UsdGH)));
6184 env(pay(trader, trader, input.sendUsdGH),
6186 path(~btcGH, ~usdGH),
6191 auto const failUsdGH = features[fixAMMv1_1] ? input.failUsdGHr : input.failUsdGH;
6192 auto const failUsdBIT = features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT;
6193 auto const goodUsdGH = features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH;
6194 auto const goodUsdBIT = features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT;
6195 auto const lpTokenBalance = [&] {
6196 if (not env.
enabled(fixAMMv1_3))
6197 return input.lpTokenBalance;
6199 return input.lpTokenBalanceAlt.value_or(input.lpTokenBalance);
6202 if (!features[fixAMMOverflowOffer])
6204 BEAST_EXPECT(amm.expectBalances(failUsdGH, failUsdBIT, lpTokenBalance));
6208 BEAST_EXPECT(amm.expectBalances(goodUsdGH, goodUsdBIT, lpTokenBalance));
6212 BEAST_EXPECT(amm.getLPTokensBalance() == preSwapLPTokenBalance);
6216 Number const sqrtPoolProduct =
root2(goodUsdGH * goodUsdBIT);
6226 BEAST_EXPECT((sqrtPoolProduct +
Number{1, -14} >= input.lpTokenBalance));