2240 using namespace jtx;
2245 features = features - featureSingleAssetVault - featureLendingProtocol;
2251 [&](
AMM& ammAlice,
Env& env) {
2252 ammAlice.
deposit(carol, 1'000'000);
2253 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
2265 [&](
AMM& ammAlice,
Env& env) {
2266 ammAlice.
deposit(carol, 1'000'000);
2268 env(ammAlice.
bid({.account = carol, .bidMin = 110, .bidMax = 110}));
2272 env(ammAlice.
bid({.account = alice, .bidMin = 180, .bidMax = 200}));
2283 [&](
AMM& ammAlice,
Env& env) {
2284 ammAlice.
deposit(carol, 1'000'000);
2286 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
2289 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
2290 ammAlice.
deposit(bob, 1'000'000);
2292 env(ammAlice.
bid({.account = bob}));
2302 env(ammAlice.
bid({.account = carol, .bidMax = 600}));
2314 env(ammAlice.
bid({.account = carol, .bidMin = 100, .bidMax = 600}));
2324 [&](
AMM& ammAlice,
Env& env) {
2325 ammAlice.
deposit(carol, 1'000'000);
2327 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
2328 ammAlice.
deposit(bob, 1'000'000);
2329 if (!features[fixAMMv1_3])
2336 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
2340 env(ammAlice.
bid({.account = bob}));
2345 env(ammAlice.
bid({.account = carol}));
2350 env(ammAlice.
bid({.account = bob}));
2355 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
2358 if (!features[fixAMMv1_3])
2374 [&](
AMM& ammAlice,
Env& env) {
2377 fund(env, gw, {bob, dan, ed}, {USD(20'000)}, Fund::Acct);
2378 ammAlice.
deposit(bob, 1'000'000);
2379 ammAlice.
deposit(ed, 1'000'000);
2380 ammAlice.
deposit(carol, 500'000);
2381 ammAlice.
deposit(dan, 500'000);
2386 .authAccounts = {bob, ed},
2388 auto const slotPrice =
IOUAmount{5'200};
2389 ammTokens -= slotPrice;
2391 if (!features[fixAMMv1_3])
2396 for (
int i = 0; i < 10; ++i)
2398 auto tokens = ammAlice.
deposit(carol, USD(100));
2399 ammAlice.
withdraw(carol, tokens, USD(0));
2400 tokens = ammAlice.
deposit(bob, USD(100));
2401 ammAlice.
withdraw(bob, tokens, USD(0));
2402 tokens = ammAlice.
deposit(ed, USD(100));
2403 ammAlice.
withdraw(ed, tokens, USD(0));
2406 if (!features[fixAMMv1_1])
2408 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'499'00572620545), -11));
2409 BEAST_EXPECT(env.
balance(bob, USD) ==
STAmount(USD, UINT64_C(18'999'00572616195), -11));
2410 BEAST_EXPECT(env.
balance(ed, USD) ==
STAmount(USD, UINT64_C(18'999'00572611841), -11));
2413 XRP(13'000),
STAmount(USD, UINT64_C(13'002'98282151419), -11), ammTokens));
2417 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'499'00572620544), -11));
2418 BEAST_EXPECT(env.
balance(bob, USD) ==
STAmount(USD, UINT64_C(18'999'00572616194), -11));
2419 BEAST_EXPECT(env.
balance(ed, USD) ==
STAmount(USD, UINT64_C(18'999'0057261184), -10));
2421 if (!features[fixAMMv1_3])
2423 XRP(13'000),
STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens));
2426 XRPAmount{13'000'000'003},
STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens));
2430 for (
int i = 0; i < 10; ++i)
2432 auto const tokens = ammAlice.
deposit(dan, USD(100));
2433 ammAlice.
withdraw(dan, tokens, USD(0));
2438 if (!features[fixAMMv1_1])
2440 BEAST_EXPECT(env.
balance(dan, USD) ==
STAmount(USD, UINT64_C(19'490'056722744), -9));
2443 XRP(13'000),
STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens));
2445 ammAlice.
deposit(carol, USD(100));
2448 XRP(13'000),
STAmount{USD, UINT64_C(13'112'92609877019), -11}, ammTokens));
2454 XRPAmount{13'100'000'668},
STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens));
2458 if (!features[fixAMMv1_3])
2459 BEAST_EXPECT(env.
balance(dan, USD) ==
STAmount(USD, UINT64_C(19'490'05672274399), -11));
2461 BEAST_EXPECT(env.
balance(dan, USD) ==
STAmount(USD, UINT64_C(19'490'05672274398), -11));
2463 if (!features[fixAMMv1_3])
2465 XRP(13'000),
STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens));
2468 XRPAmount{13'000'000'003},
STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens));
2470 ammAlice.
deposit(carol, USD(100));
2472 if (!features[fixAMMv1_3])
2474 XRP(13'000),
STAmount{USD, UINT64_C(13'112'92609877023), -11}, ammTokens));
2477 XRPAmount{13'000'000'003},
STAmount{USD, UINT64_C(13'112'92609877024), -11}, ammTokens));
2482 if (!features[fixAMMv1_3])
2484 XRPAmount{13'100'000'668},
STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens));
2487 XRPAmount{13'100'000'671},
STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens));
2495 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
2498 XRPAmount{13'000'000'668},
STAmount{USD, UINT64_C(13'114'03663047264), -11}, ammTokens));
2500 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
2503 XRPAmount{13'000'000'668},
STAmount{USD, UINT64_C(13'114'03663047269), -11}, ammTokens));
2508 XRPAmount{13'000'000'671},
STAmount{USD, UINT64_C(13'114'03663044937), -11}, ammTokens));
2514 if (!features[fixAMMv1_1])
2515 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'399'00572620545), -11));
2516 else if (!features[fixAMMv1_3])
2517 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'399'00572620544), -11));
2519 for (
int i = 0; i < 10; ++i)
2521 auto const tokens = ammAlice.
deposit(carol, USD(100));
2522 ammAlice.
withdraw(carol, tokens, USD(0));
2526 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
2528 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'389'06197177128), -11));
2530 XRPAmount{13'000'000'668},
STAmount{USD, UINT64_C(13'123'98038490681), -11}, ammTokens));
2532 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
2534 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'389'06197177124), -11));
2536 XRPAmount{13'000'000'668},
STAmount{USD, UINT64_C(13'123'98038490689), -11}, ammTokens));
2540 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'389'06197177129), -11));
2542 XRPAmount{13'000'000'671},
STAmount{USD, UINT64_C(13'123'98038488352), -11}, ammTokens));
2549 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
2552 XRPAmount(13'100'824'790),
STAmount{USD, UINT64_C(13'023'98038490681), -11}, ammTokens));
2554 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
2557 XRPAmount(13'100'824'790),
STAmount{USD, UINT64_C(13'023'98038490689), -11}, ammTokens));
2562 XRPAmount(13'100'824'793),
STAmount{USD, UINT64_C(13'023'98038488352), -11}, ammTokens));
2572 [&](AMM& ammAlice, Env& env) {
2574 auto const tiny =
Number{STAmount::cMinValue, STAmount::cMinOffset};
2575 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{tiny}}));
2578 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny}));
2580 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(10'000), ammAlice.tokens()));
2584 .bidMin = IOUAmount{STAmount::cMinValue, STAmount::cMinOffset},
2587 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny * Number{105, -2}}));
2590 BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(10'000), ammAlice.tokens()));
2599 [&](AMM& ammAlice, Env& env) {
2602 .bidMin = IOUAmount{100},
2603 .authAccounts = {carol},
2605 BEAST_EXPECT(ammAlice.expectAuctionSlot({carol}));
2606 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{100}}));
2607 BEAST_EXPECT(ammAlice.expectAuctionSlot({}));
2610 fund(env, {bob, dan},
XRP(1'000));
2613 .bidMin = IOUAmount{100},
2614 .authAccounts = {bob, dan},
2616 BEAST_EXPECT(ammAlice.expectAuctionSlot({bob, dan}));
2625 Env env(*
this, features);
2626 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
2627 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
2628 auto const lpIssue =
amm.lptIssue();
2629 env.trust(STAmount{lpIssue, 500}, alice);
2630 env.trust(STAmount{lpIssue, 50}, bob);
2631 env(
pay(gw, alice, STAmount{lpIssue, 500}));
2632 env(
pay(gw, bob, STAmount{lpIssue, 50}));
2634 env(
amm.bid({.account = alice, .bidMin = 500}));
2635 BEAST_EXPECT(
amm.expectAuctionSlot(100, 0, IOUAmount{500}));
2636 BEAST_EXPECT(
expectHolding(env, alice, STAmount{lpIssue, 0}));
2639 env(
pay(alice, bob, USD(10)), path(~USD), sendmax(
XRP(11)));
2640 BEAST_EXPECT(
amm.expectBalances(XRPAmount{1'010'010'011}, USD(1'000), IOUAmount{1'004'487'562112089, -9}));
2642 env(
pay(bob, alice,
XRP(10)), path(~XRP), sendmax(USD(11)));
2643 if (!features[fixAMMv1_1])
2645 BEAST_EXPECT(
amm.expectBalances(
2646 XRPAmount{1'000'010'011},
2647 STAmount{USD, UINT64_C(1'010'10090898081), -11},
2648 IOUAmount{1'004'487'562112089, -9}));
2652 BEAST_EXPECT(
amm.expectBalances(
2653 XRPAmount{1'000'010'011},
2654 STAmount{USD, UINT64_C(1'010'100908980811), -12},
2655 IOUAmount{1'004'487'562112089, -9}));
2661 Env env(*
this, features);
2662 auto const baseFee = env.current()->fees().base;
2664 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
2665 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
2669 auto jtx = env.jt(tx, seq(1), fee(baseFee));
2670 env.app().config().features.erase(featureAMM);
2671 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
2672 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
2673 BEAST_EXPECT(pf == temDISABLED);
2674 env.app().config().features.insert(featureAMM);
2678 auto jtx = env.jt(tx, seq(1), fee(baseFee));
2679 jtx.jv[
"TxnSignature"] =
"deadbeef";
2680 jtx.stx = env.ust(jtx);
2681 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
2682 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
2683 BEAST_EXPECT(pf != tesSUCCESS);
2687 auto jtx = env.jt(tx, seq(1), fee(baseFee));
2688 jtx.jv[
"Asset2"][
"currency"] =
"XRP";
2689 jtx.jv[
"Asset2"].removeMember(
"issuer");
2690 jtx.stx = env.ust(jtx);
2691 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
2692 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
2693 BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
2835 testcase(
"Basic Payment");
2836 using namespace jtx;
2840 features = features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol;
2845 [&](
AMM& ammAlice,
Env& env) {
2846 env.
fund(jtx::XRP(30'000), bob);
2856 {{
XRP(10'000), USD(10'100)}},
2863 [&](
AMM& ammAlice,
Env& env) {
2864 env.
fund(jtx::XRP(30'000), bob);
2866 env(pay(bob, carol, USD(100)),
sendmax(
XRP(100)));
2874 {{
XRP(10'000), USD(10'100)}},
2882 [&](
AMM& ammAlice,
Env& env) {
2883 env.
fund(jtx::XRP(30'000), bob);
2893 {{
XRP(10'000), USD(10'100)}},
2900 [&](
AMM& ammAlice,
Env& env) {
2901 env.
fund(jtx::XRP(30'000), bob);
2905 env(pay(bob, carol, USD(100)),
2919 env(pay(bob, carol, USD(100)),
2926 {{
XRP(10'000), USD(10'010)}},
2933 [&](
AMM& ammAlice,
Env& env) {
2936 env.
fund(jtx::XRP(30'000), bob);
2941 env(pay(bob, carol, USD(100)),
2951 {{
XRP(10'000), USD(10'010)}},
2958 [&](
AMM& ammAlice,
Env& env) {
2959 env.
fund(jtx::XRP(30'000), bob);
2961 env(pay(bob, carol, USD(100)),
2967 {{
XRP(10'000), USD(10'000)}},
2977 Env env(*
this, features);
2978 fund(env, gw, {alice, carol}, {USD(30'000), EUR(30'000)}, Fund::All);
2982 auto ammEUR_XRP =
AMM(env, alice,
XRP(10'000), EUR(10'000));
2983 auto ammUSD_EUR =
AMM(env, alice, EUR(10'000), USD(10'000));
2988 BEAST_EXPECT(ammEUR_XRP.expectBalances(
2989 XRPAmount(10'030'082'730),
STAmount(EUR, UINT64_C(9'970'007498125468), -12), ammEUR_XRP.tokens()));
2990 if (!features[fixAMMv1_1])
2992 BEAST_EXPECT(ammUSD_EUR.expectBalances(
2993 STAmount(USD, UINT64_C(9'970'097277662122), -12),
2994 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
2995 ammUSD_EUR.tokens()));
2998 Amounts
const expectedAmounts = env.
closed()->rules().enabled(fixReducedOffersV2)
2999 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233787816), -14)}
3000 : Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233787818), -14)};
3002 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
3006 BEAST_EXPECT(ammUSD_EUR.expectBalances(
3007 STAmount(USD, UINT64_C(9'970'097277662172), -12),
3008 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
3009 ammUSD_EUR.tokens()));
3012 Amounts
const expectedAmounts = env.
closed()->rules().enabled(fixReducedOffersV2)
3013 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233782839), -14)}
3014 : Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233782840), -14)};
3016 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
3029 [&](
AMM& ammAlice,
Env& env) {
3032 env.
trust(EUR(2'000), alice);
3034 env(pay(gw, alice, EUR(1'000)));
3049 STAmount(EUR, UINT64_C(49'98750312422), -11),
3050 STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
3066 [&](
AMM& ammAlice,
Env& env) {
3067 fund(env, gw, {bob}, {USD(100)}, Fund::Acct);
3073 if (!features[fixAMMv1_1])
3082 XRP(10'100),
STAmount(USD, UINT64_C(10'000'00000000001), -11), ammAlice.
tokens()));
3088 env, alice,
XRP(30'000) -
XRP(10'000) -
XRP(100) -
XRP(100) - ammCrtFee(env) -
txfee(env, 1)));
3091 {{
XRP(10'000), USD(10'100)}},
3100 Env env(*
this, features);
3101 fund(env, gw, {alice, bob, carol},
XRP(20'000), {USD(2'000)});
3105 AMM ammAlice(env, alice,
XRP(1'000), USD(1'050));
3115 [&](
AMM& ammAlice,
Env& env) {
3116 fund(env, gw, {bob}, {USD(1'000)}, Fund::Acct);
3118 env(offer(bob, USD(100),
XRP(100)));
3127 {{
XRP(10'000), USD(10'100)}},
3135 [&](
AMM& ammAlice,
Env& env) {
3136 env(rate(gw, 1.25));
3142 env(offer(carol, EUR(100), GBP(100)));
3152 {{GBP(1'000), EUR(1'100)}},
3158 [&](
AMM& amm,
Env& env) {
3159 env(rate(gw, 1.001));
3161 env(offer(carol,
XRP(100), USD(55)));
3163 if (!features[fixAMMv1_1])
3172 BEAST_EXPECT(amm.expectBalances(
XRP(1'000), USD(500), amm.tokens()));
3173 BEAST_EXPECT(
expectOffers(env, carol, 1, {{Amounts{
XRP(100), USD(55)}}}));
3184 BEAST_EXPECT(amm.expectBalances(
3185 XRPAmount(909'090'909),
STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens()));
3190 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(29'949'94999999494), -11));
3193 {{
XRP(1'000), USD(500)}},
3198 [&](
AMM& amm,
Env& env) {
3199 env(rate(gw, 1.001));
3201 env(offer(carol,
XRP(10), USD(5.5)));
3203 if (!features[fixAMMv1_1])
3206 amm.expectBalances(
XRP(990),
STAmount{USD, UINT64_C(505'050505050505), -12}, amm.tokens()));
3212 amm.expectBalances(
XRP(990),
STAmount{USD, UINT64_C(505'0505050505051), -13}, amm.tokens()));
3216 {{
XRP(1'000), USD(500)}},
3222 [&](
AMM& ammAlice,
Env& env) {
3224 fund(env, gw, {bob, ed},
XRP(30'000), {GBP(2'000), EUR(2'000)}, Fund::Acct);
3225 env(rate(gw, 1.25));
3236 env(offer(carol, EUR(100), GBP(100)));
3238 if (!features[fixAMMv1_1])
3247 STAmount{GBP, UINT64_C(1'037'06583722133), -11},
3248 STAmount{EUR, UINT64_C(1'060'684828792831), -12},
3256 STAmount{EUR, UINT64_C(50'684828792831), -12},
3257 STAmount{GBP, UINT64_C(50'684828792831), -12}}}));
3280 STAmount{GBP, UINT64_C(1'060'684828792832), -12},
3281 STAmount{EUR, UINT64_C(1'037'06583722134), -11},
3289 STAmount{EUR, UINT64_C(27'06583722134028), -14},
3290 STAmount{GBP, UINT64_C(27'06583722134028), -14}}}));
3308 {{GBP(1'000), EUR(1'100)}},
3321 [&](
AMM& ammAlice,
Env& env) {
3322 fund(env, gw, {bob}, {GBP(200), EUR(200)}, Fund::Acct);
3323 env(rate(gw, 1.25));
3331 {{GBP(1'000), EUR(1'100)}},
3348 [&](
AMM& ammAlice,
Env& env) {
3351 auto const CAN = gw[
"CAN"];
3352 fund(env, gw, {dan}, {CAN(200), GBP(200)}, Fund::Acct);
3353 fund(env, gw, {ed}, {EUR(200), USD(200)}, Fund::Acct);
3354 fund(env, gw, {bob}, {CAN(195.3125)}, Fund::Acct);
3355 env(trust(carol, USD(100)));
3356 env(rate(gw, 1.25));
3358 env(offer(dan, CAN(200), GBP(200)));
3359 env(offer(ed, EUR(200), USD(200)));
3361 env(pay(bob, carol, USD(100)),
3362 path(~GBP, ~EUR, ~USD),
3367 BEAST_EXPECT(
expectHolding(env, dan, CAN(356.25), GBP(43.75)));
3372 {{GBP(10'000), EUR(10'125)}},
3379 [&](
AMM& ammAlice,
Env& env) {
3380 env(pay(alice, carol, USD(99.99)),
3385 env(pay(alice, carol, USD(100)),
3390 env(pay(alice, carol,
XRP(100)),
3401 {{
XRP(100), USD(100)}},
3408 Env env(*
this, features);
3409 auto const ETH = gw[
"ETH"];
3410 fund(env, gw, {alice},
XRP(100'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
3411 fund(env, gw, {carol, bob},
XRP(1'000), {USD(200)}, Fund::Acct);
3412 AMM xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
3413 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
3414 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
3415 AMM xrp_usd(env, alice,
XRP(10'150), USD(10'200));
3416 AMM xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
3417 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
3418 AMM eur_usd(env, alice, EUR(10'100), USD(10'000));
3419 env(pay(bob, carol, USD(100)),
3420 path(~EUR, ~BTC, ~USD),
3422 path(~ETH, ~EUR, ~USD),
3424 if (!features[fixAMMv1_1])
3431 STAmount{ETH, UINT64_C(10'926'34220755506), -11},
3432 STAmount{EUR, UINT64_C(10'973'54232078752), -11},
3435 STAmount{EUR, UINT64_C(10'126'45767921248), -11},
3436 STAmount{USD, UINT64_C(9'973'93151712086), -11},
3448 STAmount{ETH, UINT64_C(10'926'34220755539), -11},
3449 STAmount{EUR, UINT64_C(10'973'5423207872), -10},
3452 STAmount{EUR, UINT64_C(10'126'4576792128), -10},
3453 STAmount{USD, UINT64_C(9'973'93151712057), -11},
3467 BEAST_EXPECT(xrp_eur.expectBalances(
XRP(10'100), EUR(10'000), xrp_eur.tokens()));
3476 Env env(*
this, features);
3477 auto const ETH = gw[
"ETH"];
3478 fund(env, gw, {alice},
XRP(40'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
3479 fund(env, gw, {carol, bob},
XRP(1000), {USD(200)}, Fund::Acct);
3480 AMM xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
3481 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
3482 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
3483 AMM xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
3484 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
3485 env(pay(bob, carol, USD(100)),
path(~EUR, ~BTC, ~USD),
path(~ETH, ~EUR, ~BTC, ~USD),
sendmax(
XRP(200)));
3486 if (!features[fixAMMv1_1])
3490 BEAST_EXPECT(xrp_eur.expectBalances(
3491 XRPAmount(10'118'738'472),
STAmount{EUR, UINT64_C(9'981'544436337968), -12}, xrp_eur.tokens()));
3493 STAmount{EUR, UINT64_C(10'101'16096785173), -11},
3494 STAmount{BTC, UINT64_C(10'097'91426968066), -11},
3497 STAmount{BTC, UINT64_C(10'202'08573031934), -11}, USD(9'900), btc_usd.
tokens()));
3501 STAmount{ETH, UINT64_C(10'982'58927221988), -11},
3502 STAmount{EUR, UINT64_C(10'917'2945958103), -10},
3507 BEAST_EXPECT(xrp_eur.expectBalances(
3508 XRPAmount(10'118'738'472),
STAmount{EUR, UINT64_C(9'981'544436337923), -12}, xrp_eur.tokens()));
3510 STAmount{EUR, UINT64_C(10'101'16096785188), -11},
3511 STAmount{BTC, UINT64_C(10'097'91426968059), -11},
3514 STAmount{BTC, UINT64_C(10'202'08573031941), -11}, USD(9'900), btc_usd.
tokens()));
3518 STAmount{ETH, UINT64_C(10'982'58927222004), -11},
3519 STAmount{EUR, UINT64_C(10'917'2945958102), -10},
3528 [&](
AMM& ammAlice,
Env& env) {
3530 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
3531 env(trust(alice, EUR(200)));
3532 for (
int i = 0; i < 30; ++i)
3533 env(offer(alice, EUR(1.0 + 0.01 * i),
XRP(1)));
3536 env(offer(alice, EUR(140),
XRP(100)));
3537 env(pay(bob, carol, USD(100)),
3541 if (!features[fixAMMv1_1])
3545 XRP(10'030),
STAmount{USD, UINT64_C(9'970'089730807577), -12}, ammAlice.
tokens()));
3551 XRP(10'030),
STAmount{USD, UINT64_C(9'970'089730807827), -12}, ammAlice.
tokens()));
3562 [&](
AMM& ammAlice,
Env& env) {
3564 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
3565 env(trust(alice, EUR(200)));
3566 for (
int i = 0; i < 29; ++i)
3567 env(offer(alice, EUR(1.0 + 0.01 * i),
XRP(1)));
3570 env(offer(alice, EUR(140),
XRP(100)));
3571 env(pay(bob, carol, USD(100)),
3576 if (!features[fixAMMv1_1])
3596 Env env(*
this, features);
3597 fund(env, gw, {alice, carol, bob},
XRP(30'000), {USD(30'000)});
3598 env(offer(bob,
XRP(100), USD(100.001)));
3599 AMM ammAlice(env, alice,
XRP(10'000), USD(10'100));
3600 env(offer(carol, USD(100),
XRP(100)));
3601 if (!features[fixAMMv1_1])
3606 env, bob, 1, {{{
XRPAmount{50'074'629},
STAmount{USD, UINT64_C(50'07513050698), -11}}}}));
3613 env, bob, 1, {{{
XRPAmount{50'074'628},
STAmount{USD, UINT64_C(50'07512950697), -11}}}}));
3620 [&](
AMM& ammAlice,
Env& env) {
3624 env(pay(alice, carol, USD(1)),
3884 testcase(
"Trading Fee");
3885 using namespace jtx;
3889 [&](
AMM& ammAlice,
Env& env) {
3891 ammAlice.
deposit(carol, USD(3'000));
3897 ammAlice.
vote(alice, 1'000);
3901 ammAlice.
deposit(carol, USD(3'000));
3905 ammAlice.
vote(alice, 0);
3910 {{USD(1'000), EUR(1'000)}},
3918 [&](
AMM& ammAlice,
Env& env) {
3923 ammAlice.
vote(alice, 0);
3925 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
3928 BEAST_EXPECT(tokensFee ==
IOUAmount(485'636'0611129, -7));
3929 BEAST_EXPECT(tokensNoFee ==
IOUAmount(487'644'85901109, -8));
3939 [&](
AMM& ammAlice,
Env& env) {
3944 ammAlice.
vote(alice, 0);
3946 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
3949 BEAST_EXPECT(tokensFee ==
IOUAmount(98'000'00000002, -8));
3950 BEAST_EXPECT(tokensNoFee ==
IOUAmount(98'475'81871545, -8));
3959 [&](
AMM& ammAlice,
Env& env) {
3961 ammAlice.
deposit(carol, USD(3'000));
3966 ammAlice.
vote(alice, 1'000);
3972 {{USD(1'000), EUR(1'000)}},
3979 [&](
AMM& ammAlice,
Env& env) {
3980 ammAlice.
deposit(carol, 1'000'000);
3983 auto const balanceAfterWithdraw = [&]() {
3984 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
3985 return STAmount(USD, UINT64_C(30'443'43891402715), -11);
3986 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
3987 return STAmount(USD, UINT64_C(30'443'43891402714), -11);
3989 return STAmount(USD, UINT64_C(30'443'43891402713), -11);
3991 BEAST_EXPECT(env.
balance(carol, USD) == balanceAfterWithdraw);
3993 auto const deposit = balanceAfterWithdraw - USD(29'000);
3994 ammAlice.
deposit(carol, deposit);
3996 ammAlice.
vote(alice, 0);
3998 auto const tokensNoFee = ammAlice.
withdraw(carol, deposit);
3999 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4000 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(30'443'43891402717), -11));
4001 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
4002 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(30'443'43891402716), -11));
4004 BEAST_EXPECT(env.
balance(carol, USD) ==
STAmount(USD, UINT64_C(30'443'43891402713), -11));
4007 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4008 BEAST_EXPECT(tokensNoFee ==
IOUAmount(746'579'80779913, -8));
4009 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
4010 BEAST_EXPECT(tokensNoFee ==
IOUAmount(746'579'80779912, -8));
4012 BEAST_EXPECT(tokensNoFee ==
IOUAmount(746'579'80779911, -8));
4013 BEAST_EXPECT(tokensFee ==
IOUAmount(750'588'23529411, -8));
4022 [&](
AMM& ammAlice,
Env& env) {
4023 fund(env, gw, {bob},
XRP(1'000), {USD(1'000), EUR(1'000)}, Fund::Acct);
4037 ammAlice.
vote(alice, 1'000);
4047 USD(1'000),
STAmount{EUR, UINT64_C(1'010'10101010101), -11}, ammAlice.
tokens()));
4049 {{USD(1'000), EUR(1'010)}},
4056 [&](
AMM& ammAlice,
Env& env) {
4058 env(offer(carol, EUR(10), USD(10)));
4063 env(offer(carol, USD(10), EUR(10)));
4066 ammAlice.
vote(alice, 500);
4068 env(offer(carol, EUR(10), USD(10)));
4079 STAmount{EUR, UINT64_C(5'025125628140703), -15},
4080 STAmount{USD, UINT64_C(5'025125628140703), -15}}}}));
4081 if (!features[fixAMMv1_1])
4084 STAmount{USD, UINT64_C(1'004'974874371859), -12},
4085 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
4091 STAmount{USD, UINT64_C(1'004'97487437186), -11},
4092 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
4096 {{USD(1'000), EUR(1'010)}},
4106 Env env(*
this, features);
4108 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4109 env(offer(carol, EUR(5), USD(5)));
4110 AMM ammAlice(env, alice, USD(1'005), EUR(1'000));
4113 if (!features[fixAMMv1_1])
4122 USD(1'000),
STAmount(EUR, UINT64_C(1005'000000000001), -12), ammAlice.
tokens()));
4130 Env env(*
this, features);
4132 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4133 env(offer(carol, EUR(5), USD(5)));
4135 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 250);
4138 if (!features[fixAMMv1_1])
4142 USD(1'000),
STAmount{EUR, UINT64_C(1'005'012546992382), -12}, ammAlice.
tokens()));
4148 USD(1'000),
STAmount{EUR, UINT64_C(1'005'012546992372), -12}, ammAlice.
tokens()));
4157 Env env(*
this, features);
4159 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4160 env(offer(carol, EUR(10), USD(10)));
4162 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
4175 Env env(*
this, features);
4177 fund(env, gw, {alice, bob, carol, ed},
XRP(1'000), {USD(2'000), EUR(2'000)});
4178 env(offer(carol, EUR(9), USD(9)));
4180 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
4185 USD(1'004),
STAmount{EUR, UINT64_C(1'001'006076703288), -12}, ammAlice.
tokens()));
4193 testcase(
"Adjusted Deposit/Withdraw Tokens");
4195 using namespace jtx;
4199 [&](
AMM& ammAlice,
Env& env) {
4207 Account const nataly(
"nataly");
4208 fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, nataly}, {USD(1'500'000)}, Fund::Acct);
4209 for (
int i = 0; i < 10; ++i)
4213 ammAlice.
deposit(simon, USD(0.1));
4215 ammAlice.
deposit(chris, USD(1));
4217 ammAlice.
deposit(dan, USD(10));
4219 ammAlice.
deposit(bob, USD(100));
4221 ammAlice.
deposit(carol, USD(1'000));
4223 ammAlice.
deposit(ed, USD(10'000));
4225 ammAlice.
deposit(paul, USD(100'000));
4227 ammAlice.
deposit(nataly, USD(1'000'000));
4233 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4236 else if (features[fixAMMv1_3])
4245 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4247 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
4253 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
4255 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
4261 if (!features[fixAMMv1_1])
4263 else if (features[fixAMMv1_3])
4279 [&](
AMM& ammAlice,
Env& env) {
4287 Account const nataly(
"nataly");
4288 fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, nataly},
XRP(2'000'000), {}, Fund::Acct);
4289 for (
int i = 0; i < 10; ++i)
4310 auto const baseFee = env.
current()->fees().base.drops();
4311 if (!features[fixAMMv1_3])
4318 auto const xrpBalance = (
XRP(2'000'000) -
txfee(env, 20)).getText();
4340 auto const xrpBalance =
XRP(2'000'000) -
txfee(env, 20) -
drops(10);
4341 auto const xrpBalanceText = xrpBalance.getText();
4515 testcase(
"Offer/Strand Selection");
4516 using namespace jtx;
4519 auto const ETH = gw1[
"ETH"];
4520 auto const CAN = gw1[
"CAN"];
4526 auto prep = [&](
Env& env,
auto gwRate,
auto gw1Rate) {
4527 fund(env, gw, {alice, carol, bob, ed},
XRP(2'000), {USD(2'000)});
4529 fund(env, gw1, {alice, carol, bob, ed}, {ETH(2'000), CAN(2'000)}, Fund::IOUOnly);
4530 env(rate(gw, gwRate));
4531 env(rate(gw1, gw1Rate));
4549 for (
auto i = 0; i < 3; ++i)
4551 Env env(*
this, features);
4552 prep(env, rates.first, rates.second);
4554 if (i == 0 || i == 2)
4560 amm.emplace(env, ed, USD(1'000), ETH(1'000));
4561 env(pay(carol, bob, USD(100)),
path(~USD),
sendmax(ETH(500)));
4566 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
4569 q[i] = Quality(Amounts{ETH(2'000) - env.
balance(carol, ETH), env.
balance(bob, USD) - USD(2'000)});
4572 BEAST_EXPECT(q[0] > q[1]);
4574 BEAST_EXPECT(q[0] == q[2]);
4581 for (
auto i = 0; i < 3; ++i)
4583 Env env(*
this, features);
4584 prep(env, rates.first, rates.second);
4586 if (i == 0 || i == 2)
4592 amm.emplace(env, ed, USD(1'000), ETH(1'000));
4593 env(offer(alice, USD(400), ETH(400)));
4598 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
4600 if (i == 0 || i == 2)
4608 BEAST_EXPECT(
expectOffers(env, alice, 1, {Amounts{USD(400), ETH(400)}}));
4619 for (
auto i = 0; i < 3; ++i)
4621 Env env(*
this, features);
4622 prep(env, rates.first, rates.second);
4624 if (i == 0 || i == 2)
4630 amm.emplace(env, ed, USD(1'000), ETH(1'000));
4631 env(pay(carol, bob, USD(100)),
path(~USD),
sendmax(ETH(500)));
4636 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
4638 if (i == 2 && !features[fixAMMv1_1])
4640 if (rates.first == 1.5)
4642 if (!features[fixAMMv1_1])
4648 STAmount{ETH, UINT64_C(378'6327949540823), -13},
4649 STAmount{USD, UINT64_C(283'9745962155617), -13}}}}));
4656 STAmount{ETH, UINT64_C(378'6327949540813), -13},
4657 STAmount{USD, UINT64_C(283'974596215561), -12}}}}));
4661 if (!features[fixAMMv1_1])
4667 STAmount{ETH, UINT64_C(325'299461620749), -12},
4668 STAmount{USD, UINT64_C(243'9745962155617), -13}}}}));
4675 STAmount{ETH, UINT64_C(325'299461620748), -12},
4676 STAmount{USD, UINT64_C(243'974596215561), -12}}}}));
4681 if (rates.first == 1.5)
4688 STAmount{ETH, UINT64_C(378'6327949540812), -13},
4689 STAmount{USD, UINT64_C(283'9745962155609), -13}}}}));
4698 STAmount{ETH, UINT64_C(325'2994616207479), -13},
4699 STAmount{USD, UINT64_C(243'9745962155609), -13}}}}));
4703 q[i] = Quality(Amounts{ETH(2'000) - env.
balance(carol, ETH), env.
balance(bob, USD) - USD(2'000)});
4706 BEAST_EXPECT(q[1] > q[0]);
4708 BEAST_EXPECT(q[2] > q[1]);
4712 for (
auto i = 0; i < 3; ++i)
4714 Env env(*
this, features);
4715 prep(env, rates.first, rates.second);
4717 if (i == 0 || i == 2)
4723 amm.emplace(env, ed, USD(1'000), ETH(1'000));
4724 env(offer(alice, USD(250), ETH(400)));
4729 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
4735 if (rates.first == 1.5)
4737 if (!features[fixAMMv1_1])
4739 BEAST_EXPECT(
expectOffers(env, ed, 1, {{Amounts{ETH(400), USD(250)}}}));
4745 STAmount{USD, UINT64_C(40'5694150420947), -13},
4746 STAmount{ETH, UINT64_C(64'91106406735152), -14},
4759 STAmount{ETH, UINT64_C(335'0889359326475), -13},
4760 STAmount{USD, UINT64_C(209'4305849579047), -13},
4767 if (!features[fixAMMv1_1])
4775 STAmount{ETH, UINT64_C(335'0889359326485), -13},
4776 STAmount{USD, UINT64_C(209'4305849579053), -13},
4788 STAmount{ETH, UINT64_C(335'0889359326475), -13},
4789 STAmount{USD, UINT64_C(209'4305849579047), -13},
4815 for (
auto i = 0; i < 3; ++i)
4817 Env env(*
this, features);
4818 prep(env, rates.first, rates.second);
4821 if (i == 0 || i == 2)
4829 amm.emplace(env, ed, ETH(1'000), USD(1'000));
4831 env(pay(carol, bob, USD(100)),
path(~USD),
path(~CAN, ~USD),
sendmax(ETH(600)));
4836 if (i == 2 && !features[fixAMMv1_1])
4838 if (rates.first == 1.5)
4841 BEAST_EXPECT(amm->expectBalances(
4842 STAmount{ETH, UINT64_C(1'176'66038955758), -11}, USD(850), amm->tokens()));
4846 BEAST_EXPECT(amm->expectBalances(
4847 STAmount{ETH, UINT64_C(1'179'540094339627), -12},
4848 STAmount{USD, UINT64_C(847'7880529867501), -13},
4855 STAmount{ETH, UINT64_C(343'3179205198749), -13},
4856 STAmount{CAN, UINT64_C(343'3179205198749), -13},
4859 STAmount{CAN, UINT64_C(362'2119470132499), -13},
4860 STAmount{USD, UINT64_C(362'2119470132499), -13},
4866 if (rates.first == 1.5)
4869 BEAST_EXPECT(amm->expectBalances(
4870 STAmount{ETH, UINT64_C(1'176'660389557593), -12}, USD(850), amm->tokens()));
4874 BEAST_EXPECT(amm->expectBalances(
4875 STAmount{ETH, UINT64_C(1'179'54009433964), -11},
4876 STAmount{USD, UINT64_C(847'7880529867501), -13},
4883 STAmount{ETH, UINT64_C(343'3179205198749), -13},
4884 STAmount{CAN, UINT64_C(343'3179205198749), -13},
4887 STAmount{CAN, UINT64_C(362'2119470132499), -13},
4888 STAmount{USD, UINT64_C(362'2119470132499), -13},
4892 q[i] = Quality(Amounts{ETH(2'000) - env.
balance(carol, ETH), env.
balance(bob, USD) - USD(2'000)});
4894 BEAST_EXPECT(q[1] > q[0]);
4895 BEAST_EXPECT(q[2] > q[0] && q[2] < q[1]);
4961 testcase(
"Fix changeSpotPriceQuality");
4962 using namespace jtx;
4967 SucceedShouldSucceedResize,
4984 {
"0.001519763260828713",
"1558701", Quality{5414253689393440221}, 1000, FailShouldSucceed},
4985 {
"0.01099814367603737",
"1892611", Quality{5482264816516900274}, 1000, FailShouldSucceed},
4986 {
"0.78",
"796599", Quality{5630392334958379008}, 1000, FailShouldSucceed},
4987 {
"105439.2955578965",
"49398693", Quality{5910869983721805038}, 400, FailShouldSucceed},
4988 {
"12408293.23445213",
"4340810521", Quality{5911611095910090752}, 997, FailShouldSucceed},
4989 {
"1892611",
"0.01099814367603737", Quality{6703103457950430139}, 1000, FailShouldSucceed},
4990 {
"423028.8508101858",
"3392804520", Quality{5837920340654162816}, 600, FailShouldSucceed},
4991 {
"44565388.41001027",
"73890647", Quality{6058976634606450001}, 1000, FailShouldSucceed},
4992 {
"66831.68494832662",
"16", Quality{6346111134641742975}, 0, FailShouldSucceed},
4993 {
"675.9287302203422",
"1242632304", Quality{5625960929244093294}, 300, FailShouldSucceed},
4994 {
"7047.112186735699",
"1649845866", Quality{5696855348026306945}, 504, FailShouldSucceed},
4995 {
"840236.4402981238",
"47419053", Quality{5982561601648018688}, 499, FailShouldSucceed},
4996 {
"992715.618909774",
"189445631733", Quality{5697835648288106944}, 815, SucceedShouldSucceedResize},
4997 {
"504636667521",
"185545883.9506651", Quality{6343802275337659280}, 503, SucceedShouldSucceedResize},
4998 {
"992706.7218636649",
"189447316000", Quality{5697835648288106944}, 797, SucceedShouldSucceedResize},
4999 {
"1.068737911388205",
"127860278877", Quality{5268604356368739396}, 293, SucceedShouldSucceedResize},
5000 {
"17932506.56880419",
"189308.6043676173", Quality{6206460598195440068}, 311, SucceedShouldSucceedResize},
5001 {
"1.066379294658174",
"128042251493", Quality{5268559341368739328}, 270, SucceedShouldSucceedResize},
5002 {
"350131413924",
"1576879.110907892", Quality{6487411636539049449}, 650,
Fail},
5003 {
"422093460",
"2.731797662057464", Quality{6702911108534394924}, 1000,
Fail},
5004 {
"76128132223",
"367172.7148422662", Quality{6487263463413514240}, 548,
Fail},
5005 {
"132701839250",
"280703770.7695443", Quality{6273750681188885075}, 562,
Fail},
5006 {
"994165.7604612011",
"189551302411", Quality{5697835592690668727}, 815,
Fail},
5007 {
"45053.33303227917",
"86612695359", Quality{5625695218943638190}, 500,
Fail},
5008 {
"199649.077043865",
"14017933007", Quality{5766034667318524880}, 324,
Fail},
5009 {
"27751824831.70903",
"78896950", Quality{6272538159621630432}, 500,
Fail},
5010 {
"225.3731275781907",
"156431793648", Quality{5477818047604078924}, 989,
Fail},
5011 {
"199649.077043865",
"14017933007", Quality{5766036094462806309}, 324,
Fail},
5012 {
"3.590272027140361",
"20677643641", Quality{5406056147042156356}, 808,
Fail},
5013 {
"1.070884664490231",
"127604712776", Quality{5268620608623825741}, 293,
Fail},
5014 {
"3272.448829820197",
"6275124076", Quality{5625710328924117902}, 81,
Fail},
5015 {
"0.009059512633902926",
"7994028", Quality{5477511954775533172}, 1000,
Fail},
5016 {
"1",
"1.0", Quality{0}, 100,
Fail},
5017 {
"1.0",
"1", Quality{0}, 100,
Fail},
5018 {
"10",
"10.0", Quality{xrpIouAmounts10_100}, 100,
Fail},
5019 {
"10.0",
"10", Quality{iouXrpAmounts10_100}, 100,
Fail},
5020 {
"69864389131",
"287631.4543025075", Quality{6487623473313516078}, 451, Succeed},
5021 {
"4328342973",
"12453825.99247381", Quality{6272522264364865181}, 997, Succeed},
5022 {
"32347017",
"7003.93031579449", Quality{6347261126087916670}, 1000, Succeed},
5023 {
"61697206161",
"36631.4583206413", Quality{6558965195382476659}, 500, Succeed},
5024 {
"1654524979",
"7028.659825511603", Quality{6487551345110052981}, 504, Succeed},
5025 {
"88621.22277293179",
"5128418948", Quality{5766347291552869205}, 380, Succeed},
5026 {
"1892611",
"0.01099814367603737", Quality{6703102780512015436}, 1000, Succeed},
5027 {
"4542.639373338766",
"24554809", Quality{5838994982188783710}, 0, Succeed},
5028 {
"5132932546",
"88542.99750172683", Quality{6419203342950054537}, 380, Succeed},
5029 {
"78929964.1549083",
"1506494795", Quality{5986890029845558688}, 589, Succeed},
5030 {
"10096561906",
"44727.72453735605", Quality{6487455290284644551}, 250, Succeed},
5031 {
"5092.219565514988",
"8768257694", Quality{5626349534958379008}, 503, Succeed},
5032 {
"1819778294",
"8305.084302902864", Quality{6487429398998540860}, 415, Succeed},
5033 {
"6970462.633911943",
"57359281", Quality{6054087899185946624}, 850, Succeed},
5034 {
"3983448845",
"2347.543644281467", Quality{6558965195382476659}, 856, Succeed},
5038 {
"771493171",
"1.243473020567508", Quality{6707566798038544272}, 100, SucceedShouldFail},
5042 boost::regex rx(
"^\\d+$");
5043 boost::smatch match;
5047 auto rules = env.
current()->rules();
5051 for (
auto const& t : tests)
5061 auto const poolInIsXRP = boost::regex_search(
std::get<0>(t), match, rx);
5062 auto const poolOutIsXRP = boost::regex_search(
std::get<1>(t), match, rx);
5063 assert(!(poolInIsXRP && poolOutIsXRP));
5064 auto const poolIn = getPool(
std::get<0>(t), poolInIsXRP);
5065 auto const poolOut = getPool(
std::get<1>(t), poolOutIsXRP);
5069 Amounts{poolIn, poolOut}, quality, tfee, env.
current()->rules(), env.
journal);
5072 if (status == SucceedShouldSucceedResize)
5074 if (!features[fixAMMv1_1])
5075 BEAST_EXPECT(Quality{*amounts} < quality);
5077 BEAST_EXPECT(Quality{*amounts} >= quality);
5079 else if (status == Succeed)
5081 if (!features[fixAMMv1_1])
5083 Quality{*amounts} >= quality ||
5086 BEAST_EXPECT(Quality{*amounts} >= quality);
5088 else if (status == FailShouldSucceed)
5090 BEAST_EXPECT(features[fixAMMv1_1] && Quality{*amounts} >= quality);
5092 else if (status == SucceedShouldFail)
5095 !features[fixAMMv1_1] && Quality{*amounts} < quality &&
5105 if (status ==
Fail && quality != Quality{0})
5107 auto tinyOffer = [&]() {
5111 return Amounts{takerPays,
swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
5113 else if (
isXRP(poolOut))
5116 return Amounts{
swapAssetOut(Amounts{poolIn, poolOut}, takerGets, tfee), takerGets};
5118 auto const takerPays = toAmount<STAmount>(
getIssue(poolIn),
Number{1, -10} * poolIn);
5119 return Amounts{takerPays,
swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
5121 BEAST_EXPECT(Quality(tinyOffer) < quality);
5123 else if (status == FailShouldSucceed)
5125 BEAST_EXPECT(!features[fixAMMv1_1]);
5127 else if (status == SucceedShouldFail)
5129 BEAST_EXPECT(features[fixAMMv1_1]);
5135 BEAST_EXPECT(!strcmp(e.
what(),
"changeSpotPriceQuality failed"));
5136 BEAST_EXPECT(!features[fixAMMv1_1] && status == FailShouldSucceed);
5144 BEAST_EXPECT(!res.has_value());
5210 using namespace jtx;
5216 Account const gatehub{
"gatehub"};
5217 Account const bitstamp{
"bitstamp"};
5218 Account const trader{
"trader"};
5219 auto const usdGH = gatehub[
"USD"];
5220 auto const btcGH = gatehub[
"BTC"];
5221 auto const usdBIT = bitstamp[
"USD"];
5225 char const* testCase;
5226 double const poolUsdBIT;
5227 double const poolUsdGH;
5240 double const offer1BtcGH = 0.1;
5241 double const offer2BtcGH = 0.1;
5242 double const offer2UsdGH = 1;
5243 double const rateBIT = 0.0;
5244 double const rateGH = 0.0;
5249 for (
auto const& input : {
5251 .testCase =
"Test Fix Overflow Offer",
5254 .sendMaxUsdBIT{usdBIT(50)},
5255 .sendUsdGH{usdGH,
uint64_t(272'455089820359), -12},
5258 .failUsdBIT{usdBIT,
uint64_t(46'47826086956522), -14},
5259 .failUsdBITr{usdBIT,
uint64_t(46'47826086956521), -14},
5260 .goodUsdGH{usdGH,
uint64_t(96'7543114220382), -13},
5261 .goodUsdGHr{usdGH,
uint64_t(96'7543114222965), -13},
5262 .goodUsdBIT{usdBIT,
uint64_t(8'464739069120721), -15},
5263 .goodUsdBITr{usdBIT,
uint64_t(8'464739069098152), -15},
5264 .lpTokenBalance = {28'61817604250837, -14},
5265 .lpTokenBalanceAlt =
IOUAmount{28'61817604250836, -14},
5273 .testCase =
"Overflow test {1, 100, 0.111}",
5276 .sendMaxUsdBIT{usdBIT(0.111)},
5277 .sendUsdGH{usdGH, 100},
5280 .failUsdBIT{usdBIT,
uint64_t(1'111), -3},
5281 .failUsdBITr{usdBIT,
uint64_t(1'111), -3},
5282 .goodUsdGH{usdGH,
uint64_t(90'04347888284115), -14},
5283 .goodUsdGHr{usdGH,
uint64_t(90'04347888284201), -14},
5284 .goodUsdBIT{usdBIT,
uint64_t(1'111), -3},
5285 .goodUsdBITr{usdBIT,
uint64_t(1'111), -3},
5286 .lpTokenBalance{10, 0},
5287 .offer1BtcGH = 1e-5,
5289 .offer2UsdGH = 1e-5,
5294 .testCase =
"Overflow test {1, 100, 1.00}",
5297 .sendMaxUsdBIT{usdBIT(1.00)},
5298 .sendUsdGH{usdGH, 100},
5301 .failUsdBIT{usdBIT,
uint64_t(2), 0},
5302 .failUsdBITr{usdBIT,
uint64_t(2), 0},
5303 .goodUsdGH{usdGH,
uint64_t(52'94379354424079), -14},
5304 .goodUsdGHr{usdGH,
uint64_t(52'94379354424135), -14},
5305 .goodUsdBIT{usdBIT,
uint64_t(2), 0},
5306 .goodUsdBITr{usdBIT,
uint64_t(2), 0},
5307 .lpTokenBalance{10, 0},
5308 .offer1BtcGH = 1e-5,
5310 .offer2UsdGH = 1e-5,
5315 .testCase =
"Overflow test {1, 100, 4.6432}",
5318 .sendMaxUsdBIT{usdBIT(4.6432)},
5319 .sendUsdGH{usdGH, 100},
5322 .failUsdBIT{usdBIT,
uint64_t(5'6432), -4},
5323 .failUsdBITr{usdBIT,
uint64_t(5'6432), -4},
5324 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
5325 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
5326 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
5327 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
5328 .lpTokenBalance{10, 0},
5329 .offer1BtcGH = 1e-5,
5331 .offer2UsdGH = 1e-5,
5336 .testCase =
"Overflow test {1, 100, 10}",
5339 .sendMaxUsdBIT{usdBIT(10)},
5340 .sendUsdGH{usdGH, 100},
5343 .failUsdBIT{usdBIT,
uint64_t(11), 0},
5344 .failUsdBITr{usdBIT,
uint64_t(11), 0},
5345 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
5346 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
5347 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
5348 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
5349 .lpTokenBalance{10, 0},
5350 .offer1BtcGH = 1e-5,
5352 .offer2UsdGH = 1e-5,
5357 .testCase =
"Overflow test {50, 100, 5.55}",
5360 .sendMaxUsdBIT{usdBIT(5.55)},
5361 .sendUsdGH{usdGH, 100},
5364 .failUsdBIT{usdBIT,
uint64_t(55'55), -2},
5365 .failUsdBITr{usdBIT,
uint64_t(55'55), -2},
5366 .goodUsdGH{usdGH,
uint64_t(90'04347888284113), -14},
5367 .goodUsdGHr{usdGH,
uint64_t(90'0434788828413), -13},
5368 .goodUsdBIT{usdBIT,
uint64_t(55'55), -2},
5369 .goodUsdBITr{usdBIT,
uint64_t(55'55), -2},
5370 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
5371 .offer1BtcGH = 1e-5,
5373 .offer2UsdGH = 1e-5,
5378 .testCase =
"Overflow test {50, 100, 50.00}",
5381 .sendMaxUsdBIT{usdBIT(50.00)},
5382 .sendUsdGH{usdGH, 100},
5383 .failUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
5384 .failUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
5385 .failUsdBIT{usdBIT,
uint64_t(100), 0},
5386 .failUsdBITr{usdBIT,
uint64_t(100), 0},
5387 .goodUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
5388 .goodUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
5389 .goodUsdBIT{usdBIT,
uint64_t(100), 0},
5390 .goodUsdBITr{usdBIT,
uint64_t(100), 0},
5391 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
5392 .offer1BtcGH = 1e-5,
5394 .offer2UsdGH = 1e-5,
5399 .testCase =
"Overflow test {50, 100, 232.16}",
5402 .sendMaxUsdBIT{usdBIT(232.16)},
5403 .sendUsdGH{usdGH, 100},
5406 .failUsdBIT{usdBIT,
uint64_t(282'16), -2},
5407 .failUsdBITr{usdBIT,
uint64_t(282'16), -2},
5408 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
5409 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
5410 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
5411 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
5412 .lpTokenBalance{70'71067811865475, -14},
5413 .offer1BtcGH = 1e-5,
5415 .offer2UsdGH = 1e-5,
5420 .testCase =
"Overflow test {50, 100, 500}",
5423 .sendMaxUsdBIT{usdBIT(500)},
5424 .sendUsdGH{usdGH, 100},
5427 .failUsdBIT{usdBIT,
uint64_t(550), 0},
5428 .failUsdBITr{usdBIT,
uint64_t(550), 0},
5429 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
5430 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
5431 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
5432 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
5433 .lpTokenBalance{70'71067811865475, -14},
5434 .offer1BtcGH = 1e-5,
5436 .offer2UsdGH = 1e-5,
5442 testcase(input.testCase);
5443 for (
auto const& features : {all - fixAMMOverflowOffer - fixAMMv1_1 - fixAMMv1_3, all})
5447 env.
fund(
XRP(5'000), gatehub, bitstamp, trader);
5450 if (input.rateGH != 0.0)
5451 env(rate(gatehub, input.rateGH));
5452 if (input.rateBIT != 0.0)
5453 env(rate(bitstamp, input.rateBIT));
5455 env(trust(trader, usdGH(10'000'000)));
5456 env(trust(trader, usdBIT(10'000'000)));
5457 env(trust(trader, btcGH(10'000'000)));
5460 env(pay(gatehub, trader, usdGH(100'000)));
5461 env(pay(gatehub, trader, btcGH(100'000)));
5462 env(pay(bitstamp, trader, usdBIT(100'000)));
5465 AMM amm{env, trader, usdGH(input.poolUsdGH), usdBIT(input.poolUsdBIT)};
5468 IOUAmount const preSwapLPTokenBalance = amm.getLPTokensBalance();
5470 env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
5471 env(offer(trader, btcGH(input.offer2BtcGH), usdGH(input.offer2UsdGH)));
5474 env(pay(trader, trader, input.sendUsdGH),
5476 path(~btcGH, ~usdGH),
5481 auto const failUsdGH = features[fixAMMv1_1] ? input.failUsdGHr : input.failUsdGH;
5482 auto const failUsdBIT = features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT;
5483 auto const goodUsdGH = features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH;
5484 auto const goodUsdBIT = features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT;
5485 auto const lpTokenBalance = env.
enabled(fixAMMv1_3) && input.lpTokenBalanceAlt
5486 ? *input.lpTokenBalanceAlt
5487 : input.lpTokenBalance;
5488 if (!features[fixAMMOverflowOffer])
5490 BEAST_EXPECT(amm.expectBalances(failUsdGH, failUsdBIT, lpTokenBalance));
5494 BEAST_EXPECT(amm.expectBalances(goodUsdGH, goodUsdBIT, lpTokenBalance));
5498 BEAST_EXPECT(amm.getLPTokensBalance() == preSwapLPTokenBalance);
5502 Number const sqrtPoolProduct =
root2(goodUsdGH * goodUsdBIT);
5512 BEAST_EXPECT((sqrtPoolProduct +
Number{1, -14} >= input.lpTokenBalance));