3033 using namespace jtx;
3040 [&](
AMM& ammAlice,
Env& env) {
3041 ammAlice.
deposit(carol, 1'000'000);
3042 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
3055 [&](
AMM& ammAlice,
Env& env) {
3056 ammAlice.
deposit(carol, 1'000'000);
3059 {.account = carol, .bidMin = 110, .bidMax = 110}));
3065 {.account = alice, .bidMin = 180, .bidMax = 200}));
3068 XRP(11'000), USD(11'000),
IOUAmount{10'999'814'5, -1}));
3077 [&](
AMM& ammAlice,
Env& env) {
3078 ammAlice.
deposit(carol, 1'000'000);
3080 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
3083 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
3084 ammAlice.
deposit(bob, 1'000'000);
3086 env(ammAlice.
bid({.account = bob}));
3097 env(ammAlice.
bid({.account = carol, .bidMax = 600}));
3111 {.account = carol, .bidMin = 100, .bidMax = 600}));
3122 [&](
AMM& ammAlice,
Env& env) {
3123 ammAlice.
deposit(carol, 1'000'000);
3125 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
3126 ammAlice.
deposit(bob, 1'000'000);
3127 if (!features[fixAMMv1_3])
3137 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
3141 env(ammAlice.
bid({.account = bob}));
3147 env(ammAlice.
bid({.account = carol}));
3153 env(ammAlice.
bid({.account = bob}));
3162 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
3166 if (!features[fixAMMv1_3])
3187 [&](
AMM& ammAlice,
Env& env) {
3190 fund(env, gw, {bob, dan, ed}, {USD(20'000)}, Fund::Acct);
3191 ammAlice.
deposit(bob, 1'000'000);
3192 ammAlice.
deposit(ed, 1'000'000);
3193 ammAlice.
deposit(carol, 500'000);
3194 ammAlice.
deposit(dan, 500'000);
3199 .authAccounts = {bob, ed},
3201 auto const slotPrice =
IOUAmount{5'200};
3202 ammTokens -= slotPrice;
3204 if (!features[fixAMMv1_3])
3206 XRP(13'000), USD(13'000), ammTokens));
3209 XRPAmount{13'000'000'003}, USD(13'000), ammTokens));
3211 for (
int i = 0; i < 10; ++i)
3213 auto tokens = ammAlice.
deposit(carol, USD(100));
3214 ammAlice.
withdraw(carol, tokens, USD(0));
3215 tokens = ammAlice.
deposit(bob, USD(100));
3216 ammAlice.
withdraw(bob, tokens, USD(0));
3217 tokens = ammAlice.
deposit(ed, USD(100));
3218 ammAlice.
withdraw(ed, tokens, USD(0));
3221 if (!features[fixAMMv1_1])
3225 STAmount(USD, UINT64_C(29'499'00572620545), -11));
3228 STAmount(USD, UINT64_C(18'999'00572616195), -11));
3231 STAmount(USD, UINT64_C(18'999'00572611841), -11));
3235 STAmount(USD, UINT64_C(13'002'98282151419), -11),
3242 STAmount(USD, UINT64_C(29'499'00572620544), -11));
3245 STAmount(USD, UINT64_C(18'999'00572616194), -11));
3248 STAmount(USD, UINT64_C(18'999'0057261184), -10));
3250 if (!features[fixAMMv1_3])
3253 STAmount(USD, UINT64_C(13'002'98282151422), -11),
3258 STAmount(USD, UINT64_C(13'002'98282151422), -11),
3263 for (
int i = 0; i < 10; ++i)
3265 auto const tokens = ammAlice.
deposit(dan, USD(100));
3266 ammAlice.
withdraw(dan, tokens, USD(0));
3271 if (!features[fixAMMv1_1])
3275 STAmount(USD, UINT64_C(19'490'056722744), -9));
3279 STAmount{USD, UINT64_C(13'012'92609877019), -11},
3282 ammAlice.
deposit(carol, USD(100));
3286 STAmount{USD, UINT64_C(13'112'92609877019), -11},
3288 env(pay(carol, bob, USD(100)),
3296 STAmount{USD, UINT64_C(13'012'92609877019), -11},
3301 if (!features[fixAMMv1_3])
3304 STAmount(USD, UINT64_C(19'490'05672274399), -11));
3308 STAmount(USD, UINT64_C(19'490'05672274398), -11));
3310 if (!features[fixAMMv1_3])
3313 STAmount{USD, UINT64_C(13'012'92609877023), -11},
3318 STAmount{USD, UINT64_C(13'012'92609877024), -11},
3321 ammAlice.
deposit(carol, USD(100));
3323 if (!features[fixAMMv1_3])
3326 STAmount{USD, UINT64_C(13'112'92609877023), -11},
3331 STAmount{USD, UINT64_C(13'112'92609877024), -11},
3333 env(pay(carol, bob, USD(100)),
3339 if (!features[fixAMMv1_3])
3342 STAmount{USD, UINT64_C(13'012'92609877023), -11},
3347 STAmount{USD, UINT64_C(13'012'92609877024), -11},
3356 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
3360 STAmount{USD, UINT64_C(13'114'03663047264), -11},
3363 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
3367 STAmount{USD, UINT64_C(13'114'03663047269), -11},
3374 STAmount{USD, UINT64_C(13'114'03663044937), -11},
3381 if (!features[fixAMMv1_1])
3384 STAmount(USD, UINT64_C(29'399'00572620545), -11));
3385 else if (!features[fixAMMv1_3])
3388 STAmount(USD, UINT64_C(29'399'00572620544), -11));
3390 for (
int i = 0; i < 10; ++i)
3392 auto const tokens = ammAlice.
deposit(carol, USD(100));
3393 ammAlice.
withdraw(carol, tokens, USD(0));
3397 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
3401 STAmount(USD, UINT64_C(29'389'06197177128), -11));
3404 STAmount{USD, UINT64_C(13'123'98038490681), -11},
3407 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
3411 STAmount(USD, UINT64_C(29'389'06197177124), -11));
3414 STAmount{USD, UINT64_C(13'123'98038490689), -11},
3421 STAmount(USD, UINT64_C(29'389'06197177129), -11));
3424 STAmount{USD, UINT64_C(13'123'98038488352), -11},
3432 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
3436 STAmount{USD, UINT64_C(13'023'98038490681), -11},
3439 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
3443 STAmount{USD, UINT64_C(13'023'98038490689), -11},
3450 STAmount{USD, UINT64_C(13'023'98038488352), -11},
3461 [&](AMM& ammAlice, Env& env) {
3464 Number{STAmount::cMinValue, STAmount::cMinOffset};
3466 {.account = alice, .bidMin = IOUAmount{tiny}}));
3469 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny}));
3471 BEAST_EXPECT(ammAlice.expectBalances(
3472 XRP(10'000), USD(10'000), ammAlice.tokens()));
3477 IOUAmount{STAmount::cMinValue, STAmount::cMinOffset},
3480 BEAST_EXPECT(ammAlice.expectAuctionSlot(
3481 0, 0, IOUAmount{tiny * Number{105, -2}}));
3484 BEAST_EXPECT(ammAlice.expectBalances(
3485 XRP(10'000), USD(10'000), ammAlice.tokens()));
3494 [&](AMM& ammAlice, Env& env) {
3497 .bidMin = IOUAmount{100},
3498 .authAccounts = {carol},
3500 BEAST_EXPECT(ammAlice.expectAuctionSlot({carol}));
3501 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{100}}));
3502 BEAST_EXPECT(ammAlice.expectAuctionSlot({}));
3505 fund(env, {bob, dan},
XRP(1'000));
3508 .bidMin = IOUAmount{100},
3509 .authAccounts = {bob, dan},
3511 BEAST_EXPECT(ammAlice.expectAuctionSlot({bob, dan}));
3520 Env env(*
this, features);
3521 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
3522 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
3523 auto const lpIssue =
amm.lptIssue();
3524 env.trust(STAmount{lpIssue, 500}, alice);
3525 env.trust(STAmount{lpIssue, 50}, bob);
3526 env(
pay(gw, alice, STAmount{lpIssue, 500}));
3527 env(
pay(gw, bob, STAmount{lpIssue, 50}));
3529 env(
amm.bid({.account = alice, .bidMin = 500}));
3530 BEAST_EXPECT(
amm.expectAuctionSlot(100, 0, IOUAmount{500}));
3531 BEAST_EXPECT(
expectHolding(env, alice, STAmount{lpIssue, 0}));
3534 env(
pay(alice, bob, USD(10)), path(~USD), sendmax(
XRP(11)));
3535 BEAST_EXPECT(
amm.expectBalances(
3536 XRPAmount{1'010'010'011},
3538 IOUAmount{1'004'487'562112089, -9}));
3540 env(
pay(bob, alice,
XRP(10)), path(~XRP), sendmax(USD(11)));
3541 if (!features[fixAMMv1_1])
3543 BEAST_EXPECT(
amm.expectBalances(
3544 XRPAmount{1'000'010'011},
3545 STAmount{USD, UINT64_C(1'010'10090898081), -11},
3546 IOUAmount{1'004'487'562112089, -9}));
3550 BEAST_EXPECT(
amm.expectBalances(
3551 XRPAmount{1'000'010'011},
3552 STAmount{USD, UINT64_C(1'010'100908980811), -12},
3553 IOUAmount{1'004'487'562112089, -9}));
3559 Env env(*
this, features);
3560 auto const baseFee = env.current()->fees().base;
3562 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
3563 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
3567 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3568 env.app().config().features.erase(featureAMM);
3569 PreflightContext pfctx(
3572 env.current()->rules(),
3575 auto pf = AMMBid::preflight(pfctx);
3576 BEAST_EXPECT(pf == temDISABLED);
3577 env.app().config().features.insert(featureAMM);
3581 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3582 jtx.jv[
"TxnSignature"] =
"deadbeef";
3583 jtx.stx = env.ust(jtx);
3584 PreflightContext pfctx(
3587 env.current()->rules(),
3590 auto pf = AMMBid::preflight(pfctx);
3591 BEAST_EXPECT(pf != tesSUCCESS);
3595 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3596 jtx.jv[
"Asset2"][
"currency"] =
"XRP";
3597 jtx.jv[
"Asset2"].removeMember(
"issuer");
3598 jtx.stx = env.ust(jtx);
3599 PreflightContext pfctx(
3602 env.current()->rules(),
3605 auto pf = AMMBid::preflight(pfctx);
3606 BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
3777 testcase(
"Basic Payment");
3778 using namespace jtx;
3783 [&](
AMM& ammAlice,
Env& env) {
3784 env.
fund(jtx::XRP(30'000), bob);
3786 env(pay(bob, carol, USD(100)),
3792 XRP(10'100), USD(10'000), ammAlice.
tokens()));
3797 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
3799 {{
XRP(10'000), USD(10'100)}},
3806 [&](
AMM& ammAlice,
Env& env) {
3807 env.
fund(jtx::XRP(30'000), bob);
3809 env(pay(bob, carol, USD(100)),
sendmax(
XRP(100)));
3812 XRP(10'100), USD(10'000), ammAlice.
tokens()));
3817 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
3819 {{
XRP(10'000), USD(10'100)}},
3827 [&](
AMM& ammAlice,
Env& env) {
3828 env.
fund(jtx::XRP(30'000), bob);
3833 XRP(10'100), USD(10'000), ammAlice.
tokens()));
3838 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
3840 {{
XRP(10'000), USD(10'100)}},
3847 [&](
AMM& ammAlice,
Env& env) {
3848 env.
fund(jtx::XRP(30'000), bob);
3852 env(pay(bob, carol, USD(100)),
3859 XRP(10'010), USD(10'000), ammAlice.
tokens()));
3865 env, bob,
XRP(30'000) -
XRP(10) -
txfee(env, 1)));
3869 env(pay(bob, carol, USD(100)),
3877 {{
XRP(10'000), USD(10'010)}},
3884 [&](
AMM& ammAlice,
Env& env) {
3887 env.
fund(jtx::XRP(30'000), bob);
3892 env(pay(bob, carol, USD(100)),
3899 XRP(10'010), USD(10'000), ammAlice.
tokens()));
3904 STAmount{USD, UINT64_C(30'009'09090909091), -11}));
3906 env, bob,
XRP(30'000) -
XRP(10) -
txfee(env, 1)));
3908 {{
XRP(10'000), USD(10'010)}},
3915 [&](
AMM& ammAlice,
Env& env) {
3916 env.
fund(jtx::XRP(30'000), bob);
3918 env(pay(bob, carol, USD(100)),
3924 {{
XRP(10'000), USD(10'000)}},
3934 Env env(*
this, features);
3936 env, gw, {alice, carol}, {USD(30'000), EUR(30'000)}, Fund::All);
3940 auto ammEUR_XRP =
AMM(env, alice,
XRP(10'000), EUR(10'000));
3941 auto ammUSD_EUR =
AMM(env, alice, EUR(10'000), USD(10'000));
3944 env(pay(bob, carol, USD(100)),
3949 BEAST_EXPECT(ammEUR_XRP.expectBalances(
3951 STAmount(EUR, UINT64_C(9'970'007498125468), -12),
3952 ammEUR_XRP.tokens()));
3953 if (!features[fixAMMv1_1])
3955 BEAST_EXPECT(ammUSD_EUR.expectBalances(
3956 STAmount(USD, UINT64_C(9'970'097277662122), -12),
3957 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
3958 ammUSD_EUR.tokens()));
3961 Amounts
const expectedAmounts =
3962 env.
closed()->rules().enabled(fixReducedOffersV2)
3963 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233787816), -14)}
3966 STAmount(USD, UINT64_C(29'90272233787818), -14)};
3968 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
3972 BEAST_EXPECT(ammUSD_EUR.expectBalances(
3973 STAmount(USD, UINT64_C(9'970'097277662172), -12),
3974 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
3975 ammUSD_EUR.tokens()));
3978 Amounts
const expectedAmounts =
3979 env.
closed()->rules().enabled(fixReducedOffersV2)
3980 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233782839), -14)}
3983 STAmount(USD, UINT64_C(29'90272233782840), -14)};
3985 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
4001 [&](
AMM& ammAlice,
Env& env) {
4004 env.
trust(EUR(2'000), alice);
4006 env(pay(gw, alice, EUR(1'000)));
4011 env(pay(bob, carol, USD(100)),
4018 STAmount(USD, UINT64_C(9'950'01249687578), -11),
4026 STAmount(EUR, UINT64_C(49'98750312422), -11)},
4028 STAmount(EUR, UINT64_C(49'98750312422), -11),
4029 STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
4034 STAmount{USD, UINT64_C(30'099'99999999999), -11}));
4051 [&](
AMM& ammAlice,
Env& env) {
4052 fund(env, gw, {bob}, {USD(100)}, Fund::Acct);
4056 env(pay(alice, carol, USD(200)),
4060 if (!features[fixAMMv1_1])
4063 XRP(10'100), USD(10'000), ammAlice.
tokens()));
4071 STAmount(USD, UINT64_C(10'000'00000000001), -11),
4076 STAmount(USD, UINT64_C(30'199'99999999999), -11)));
4084 ammCrtFee(env) -
txfee(env, 1)));
4087 {{
XRP(10'000), USD(10'100)}},
4096 Env env(*
this, features);
4097 fund(env, gw, {alice, bob, carol},
XRP(20'000), {USD(2'000)});
4101 AMM ammAlice(env, alice,
XRP(1'000), USD(1'050));
4102 env(pay(alice, carol, USD(200)),
4107 XRP(1'050), USD(1'000), ammAlice.
tokens()));
4114 [&](
AMM& ammAlice,
Env& env) {
4115 fund(env, gw, {bob}, {USD(1'000)}, Fund::Acct);
4117 env(offer(bob, USD(100),
XRP(100)));
4120 XRP(10'100), USD(10'000), ammAlice.
tokens()));
4125 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
4128 {{
XRP(10'000), USD(10'100)}},
4136 [&](
AMM& ammAlice,
Env& env) {
4137 env(rate(gw, 1.25));
4143 env(offer(carol, EUR(100), GBP(100)));
4147 GBP(1'100), EUR(1'000), ammAlice.
tokens()));
4154 {{GBP(1'000), EUR(1'100)}},
4160 [&](
AMM& amm,
Env& env) {
4161 env(rate(gw, 1.001));
4163 env(offer(carol,
XRP(100), USD(55)));
4165 if (!features[fixAMMv1_1])
4175 amm.expectBalances(
XRP(1'000), USD(500), amm.tokens()));
4177 env, carol, 1, {{Amounts{
XRP(100), USD(55)}}}));
4188 BEAST_EXPECT(amm.expectBalances(
4190 STAmount{USD, UINT64_C(550'000000055), -9},
4199 STAmount{USD, 4'99999995, -8}}}}));
4203 STAmount(USD, UINT64_C(29'949'94999999494), -11));
4206 {{
XRP(1'000), USD(500)}},
4211 [&](
AMM& amm,
Env& env) {
4212 env(rate(gw, 1.001));
4214 env(offer(carol,
XRP(10), USD(5.5)));
4216 if (!features[fixAMMv1_1])
4218 BEAST_EXPECT(amm.expectBalances(
4220 STAmount{USD, UINT64_C(505'050505050505), -12},
4226 BEAST_EXPECT(amm.expectBalances(
4228 STAmount{USD, UINT64_C(505'0505050505051), -13},
4233 {{
XRP(1'000), USD(500)}},
4239 [&](
AMM& ammAlice,
Env& env) {
4246 {GBP(2'000), EUR(2'000)},
4248 env(rate(gw, 1.25));
4259 env(offer(carol, EUR(100), GBP(100)));
4261 if (!features[fixAMMv1_1])
4270 STAmount{GBP, UINT64_C(1'037'06583722133), -11},
4271 STAmount{EUR, UINT64_C(1'060'684828792831), -12},
4279 STAmount{EUR, UINT64_C(50'684828792831), -12},
4280 STAmount{GBP, UINT64_C(50'684828792831), -12}}}));
4292 STAmount{GBP, UINT64_C(29'941'16770347333), -11}));
4297 STAmount{EUR, UINT64_C(30'049'31517120716), -11}));
4309 STAmount{GBP, UINT64_C(1'060'684828792832), -12},
4310 STAmount{EUR, UINT64_C(1'037'06583722134), -11},
4318 STAmount{EUR, UINT64_C(27'06583722134028), -14},
4319 STAmount{GBP, UINT64_C(27'06583722134028), -14}}}));
4331 STAmount{GBP, UINT64_C(29'911'64396400896), -11}));
4336 STAmount{EUR, UINT64_C(30'072'93416277865), -11}));
4343 {{GBP(1'000), EUR(1'100)}},
4356 [&](
AMM& ammAlice,
Env& env) {
4357 fund(env, gw, {bob}, {GBP(200), EUR(200)}, Fund::Acct);
4358 env(rate(gw, 1.25));
4360 env(pay(bob, carol, EUR(100)),
4366 GBP(1'100), EUR(1'000), ammAlice.
tokens()));
4370 {{GBP(1'000), EUR(1'100)}},
4387 [&](
AMM& ammAlice,
Env& env) {
4390 auto const CAN = gw[
"CAN"];
4391 fund(env, gw, {dan}, {CAN(200), GBP(200)}, Fund::Acct);
4392 fund(env, gw, {ed}, {EUR(200), USD(200)}, Fund::Acct);
4393 fund(env, gw, {bob}, {CAN(195.3125)}, Fund::Acct);
4394 env(trust(carol, USD(100)));
4395 env(rate(gw, 1.25));
4397 env(offer(dan, CAN(200), GBP(200)));
4398 env(offer(ed, EUR(200), USD(200)));
4400 env(pay(bob, carol, USD(100)),
4401 path(~GBP, ~EUR, ~USD),
4406 BEAST_EXPECT(
expectHolding(env, dan, CAN(356.25), GBP(43.75)));
4408 GBP(10'125), EUR(10'000), ammAlice.
tokens()));
4412 {{GBP(10'000), EUR(10'125)}},
4419 [&](
AMM& ammAlice,
Env& env) {
4420 env(pay(alice, carol, USD(99.99)),
4425 env(pay(alice, carol, USD(100)),
4430 env(pay(alice, carol,
XRP(100)),
4441 {{
XRP(100), USD(100)}},
4448 Env env(*
this, features);
4449 auto const ETH = gw[
"ETH"];
4455 {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
4456 fund(env, gw, {carol, bob},
XRP(1'000), {USD(200)}, Fund::Acct);
4457 AMM xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
4458 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
4459 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
4460 AMM xrp_usd(env, alice,
XRP(10'150), USD(10'200));
4461 AMM xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
4462 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
4463 AMM eur_usd(env, alice, EUR(10'100), USD(10'000));
4464 env(pay(bob, carol, USD(100)),
4465 path(~EUR, ~BTC, ~USD),
4467 path(~ETH, ~EUR, ~USD),
4469 if (!features[fixAMMv1_1])
4475 STAmount{ETH, UINT64_C(10'073'65779244494), -11},
4478 STAmount{ETH, UINT64_C(10'926'34220755506), -11},
4479 STAmount{EUR, UINT64_C(10'973'54232078752), -11},
4482 STAmount{EUR, UINT64_C(10'126'45767921248), -11},
4483 STAmount{USD, UINT64_C(9'973'93151712086), -11},
4489 STAmount{USD, UINT64_C(10'126'06848287914), -11},
4496 STAmount{ETH, UINT64_C(10'073'65779244461), -11},
4499 STAmount{ETH, UINT64_C(10'926'34220755539), -11},
4500 STAmount{EUR, UINT64_C(10'973'5423207872), -10},
4503 STAmount{EUR, UINT64_C(10'126'4576792128), -10},
4504 STAmount{USD, UINT64_C(9'973'93151712057), -11},
4510 STAmount{USD, UINT64_C(10'126'06848287943), -11},
4520 BEAST_EXPECT(xrp_eur.expectBalances(
4521 XRP(10'100), EUR(10'000), xrp_eur.tokens()));
4523 EUR(10'000), BTC(10'200), eur_btc.
tokens()));
4525 BTC(10'100), USD(10'000), btc_usd.
tokens()));
4532 Env env(*
this, features);
4533 auto const ETH = gw[
"ETH"];
4539 {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
4540 fund(env, gw, {carol, bob},
XRP(1000), {USD(200)}, Fund::Acct);
4541 AMM xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
4542 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
4543 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
4544 AMM xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
4545 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
4546 env(pay(bob, carol, USD(100)),
4547 path(~EUR, ~BTC, ~USD),
4548 path(~ETH, ~EUR, ~BTC, ~USD),
4550 if (!features[fixAMMv1_1])
4554 BEAST_EXPECT(xrp_eur.expectBalances(
4556 STAmount{EUR, UINT64_C(9'981'544436337968), -12},
4559 STAmount{EUR, UINT64_C(10'101'16096785173), -11},
4560 STAmount{BTC, UINT64_C(10'097'91426968066), -11},
4563 STAmount{BTC, UINT64_C(10'202'08573031934), -11},
4568 STAmount{ETH, UINT64_C(10'017'41072778012), -11},
4571 STAmount{ETH, UINT64_C(10'982'58927221988), -11},
4572 STAmount{EUR, UINT64_C(10'917'2945958103), -10},
4577 BEAST_EXPECT(xrp_eur.expectBalances(
4579 STAmount{EUR, UINT64_C(9'981'544436337923), -12},
4582 STAmount{EUR, UINT64_C(10'101'16096785188), -11},
4583 STAmount{BTC, UINT64_C(10'097'91426968059), -11},
4586 STAmount{BTC, UINT64_C(10'202'08573031941), -11},
4591 STAmount{ETH, UINT64_C(10'017'41072777996), -11},
4594 STAmount{ETH, UINT64_C(10'982'58927222004), -11},
4595 STAmount{EUR, UINT64_C(10'917'2945958102), -10},
4604 [&](
AMM& ammAlice,
Env& env) {
4606 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
4607 env(trust(alice, EUR(200)));
4608 for (
int i = 0; i < 30; ++i)
4609 env(offer(alice, EUR(1.0 + 0.01 * i),
XRP(1)));
4612 env(offer(alice, EUR(140),
XRP(100)));
4613 env(pay(bob, carol, USD(100)),
4617 if (!features[fixAMMv1_1])
4622 STAmount{USD, UINT64_C(9'970'089730807577), -12},
4627 STAmount{USD, UINT64_C(30'029'91026919241), -11}));
4633 STAmount{USD, UINT64_C(9'970'089730807827), -12},
4638 STAmount{USD, UINT64_C(30'029'91026919217), -11}));
4649 [&](
AMM& ammAlice,
Env& env) {
4651 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
4652 env(trust(alice, EUR(200)));
4653 for (
int i = 0; i < 29; ++i)
4654 env(offer(alice, EUR(1.0 + 0.01 * i),
XRP(1)));
4657 env(offer(alice, EUR(140),
XRP(100)));
4658 env(pay(bob, carol, USD(100)),
4664 if (!features[fixAMMv1_1])
4670 STAmount{USD, UINT64_C(30'099'99999999999), -11}));
4680 {{{
STAmount{EUR, UINT64_C(39'1858572), -7},
4691 Env env(*
this, features);
4692 fund(env, gw, {alice, carol, bob},
XRP(30'000), {USD(30'000)});
4693 env(offer(bob,
XRP(100), USD(100.001)));
4694 AMM ammAlice(env, alice,
XRP(10'000), USD(10'100));
4695 env(offer(carol, USD(100),
XRP(100)));
4696 if (!features[fixAMMv1_1])
4700 STAmount{USD, UINT64_C(10'049'92586949302), -11},
4707 STAmount{USD, UINT64_C(50'07513050698), -11}}}}));
4713 STAmount{USD, UINT64_C(10'049'92587049303), -11},
4720 STAmount{USD, UINT64_C(50'07512950697), -11}}}}));
4727 [&](
AMM& ammAlice,
Env& env) {
4731 env(pay(alice, carol, USD(1)),
5027 testcase(
"Trading Fee");
5028 using namespace jtx;
5032 [&](
AMM& ammAlice,
Env& env) {
5034 ammAlice.
deposit(carol, USD(3'000));
5040 ammAlice.
vote(alice, 1'000);
5044 ammAlice.
deposit(carol, USD(3'000));
5046 carol,
IOUAmount{994'981155689671, -12}));
5049 ammAlice.
vote(alice, 0);
5055 STAmount{USD, UINT64_C(29'994'96220068281), -11}));
5057 {{USD(1'000), EUR(1'000)}},
5065 [&](
AMM& ammAlice,
Env& env) {
5067 auto tokensFee = ammAlice.
deposit(
5071 ammAlice.
vote(alice, 0);
5073 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
5076 BEAST_EXPECT(tokensFee ==
IOUAmount(485'636'0611129, -7));
5077 BEAST_EXPECT(tokensNoFee ==
IOUAmount(487'644'85901109, -8));
5087 [&](
AMM& ammAlice,
Env& env) {
5089 auto const tokensFee = ammAlice.
deposit(
5093 ammAlice.
vote(alice, 0);
5095 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
5098 BEAST_EXPECT(tokensFee ==
IOUAmount(98'000'00000002, -8));
5099 BEAST_EXPECT(tokensNoFee ==
IOUAmount(98'475'81871545, -8));
5108 [&](
AMM& ammAlice,
Env& env) {
5110 ammAlice.
deposit(carol, USD(3'000));
5115 ammAlice.
vote(alice, 1'000);
5122 STAmount{USD, UINT64_C(29'994'97487437186), -11}));
5124 {{USD(1'000), EUR(1'000)}},
5131 [&](
AMM& ammAlice,
Env& env) {
5132 ammAlice.
deposit(carol, 1'000'000);
5133 auto const tokensFee = ammAlice.
withdraw(
5136 auto const balanceAfterWithdraw = [&]() {
5137 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
5138 return STAmount(USD, UINT64_C(30'443'43891402715), -11);
5139 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
5140 return STAmount(USD, UINT64_C(30'443'43891402714), -11);
5142 return STAmount(USD, UINT64_C(30'443'43891402713), -11);
5144 BEAST_EXPECT(env.
balance(carol, USD) == balanceAfterWithdraw);
5146 auto const deposit = balanceAfterWithdraw - USD(29'000);
5147 ammAlice.
deposit(carol, deposit);
5149 ammAlice.
vote(alice, 0);
5151 auto const tokensNoFee = ammAlice.
withdraw(carol, deposit);
5152 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
5155 STAmount(USD, UINT64_C(30'443'43891402717), -11));
5156 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
5159 STAmount(USD, UINT64_C(30'443'43891402716), -11));
5163 STAmount(USD, UINT64_C(30'443'43891402713), -11));
5166 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
5168 tokensNoFee ==
IOUAmount(746'579'80779913, -8));
5169 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
5171 tokensNoFee ==
IOUAmount(746'579'80779912, -8));
5174 tokensNoFee ==
IOUAmount(746'579'80779911, -8));
5175 BEAST_EXPECT(tokensFee ==
IOUAmount(750'588'23529411, -8));
5184 [&](
AMM& ammAlice,
Env& env) {
5190 {USD(1'000), EUR(1'000)},
5197 env(pay(carol, alice, EUR(10)),
5208 ammAlice.
vote(alice, 1'000);
5211 env(pay(bob, carol, USD(10)),
5218 env, bob,
STAmount{EUR, UINT64_C(989'8989898989899), -13}));
5223 STAmount{EUR, UINT64_C(1'010'10101010101), -11},
5226 {{USD(1'000), EUR(1'010)}},
5233 [&](
AMM& ammAlice,
Env& env) {
5235 env(offer(carol, EUR(10), USD(10)));
5240 env(offer(carol, USD(10), EUR(10)));
5243 ammAlice.
vote(alice, 500);
5245 env(offer(carol, EUR(10), USD(10)));
5252 STAmount{USD, UINT64_C(29'995'02512562814), -11}));
5256 STAmount{EUR, UINT64_C(30'004'97487437186), -11}));
5262 STAmount{EUR, UINT64_C(5'025125628140703), -15},
5263 STAmount{USD, UINT64_C(5'025125628140703), -15}}}}));
5264 if (!features[fixAMMv1_1])
5267 STAmount{USD, UINT64_C(1'004'974874371859), -12},
5268 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
5274 STAmount{USD, UINT64_C(1'004'97487437186), -11},
5275 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
5279 {{USD(1'000), EUR(1'010)}},
5289 Env env(*
this, features);
5294 {alice, bob, carol, ed},
5296 {USD(2'000), EUR(2'000)});
5297 env(offer(carol, EUR(5), USD(5)));
5298 AMM ammAlice(env, alice, USD(1'005), EUR(1'000));
5299 env(pay(bob, ed, USD(10)),
5304 if (!features[fixAMMv1_1])
5308 USD(1'000), EUR(1'005), ammAlice.
tokens()));
5313 env, bob,
STAmount(EUR, UINT64_C(1989'999999999999), -12)));
5316 STAmount(EUR, UINT64_C(1005'000000000001), -12),
5325 Env env(*
this, features);
5330 {alice, bob, carol, ed},
5332 {USD(2'000), EUR(2'000)});
5333 env(offer(carol, EUR(5), USD(5)));
5335 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 250);
5336 env(pay(bob, ed, USD(10)),
5341 if (!features[fixAMMv1_1])
5346 STAmount{EUR, UINT64_C(1'989'987453007618), -12}));
5349 STAmount{EUR, UINT64_C(1'005'012546992382), -12},
5357 STAmount{EUR, UINT64_C(1'989'987453007628), -12}));
5360 STAmount{EUR, UINT64_C(1'005'012546992372), -12},
5370 Env env(*
this, features);
5375 {alice, bob, carol, ed},
5377 {USD(2'000), EUR(2'000)});
5378 env(offer(carol, EUR(10), USD(10)));
5380 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
5381 env(pay(bob, ed, USD(10)),
5388 USD(1'005), EUR(1'000), ammAlice.
tokens()));
5397 Env env(*
this, features);
5402 {alice, bob, carol, ed},
5404 {USD(2'000), EUR(2'000)});
5405 env(offer(carol, EUR(9), USD(9)));
5407 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
5408 env(pay(bob, ed, USD(10)),
5414 env, bob,
STAmount{EUR, UINT64_C(1'989'993923296712), -12}));
5417 STAmount{EUR, UINT64_C(1'001'006076703288), -12},
5825 testcase(
"Offer/Strand Selection");
5826 using namespace jtx;
5829 auto const ETH = gw1[
"ETH"];
5830 auto const CAN = gw1[
"CAN"];
5836 auto prep = [&](
Env& env,
auto gwRate,
auto gw1Rate) {
5837 fund(env, gw, {alice, carol, bob, ed},
XRP(2'000), {USD(2'000)});
5842 {alice, carol, bob, ed},
5843 {ETH(2'000), CAN(2'000)},
5845 env(rate(gw, gwRate));
5846 env(rate(gw1, gw1Rate));
5850 for (
auto const& rates :
5865 for (
auto i = 0; i < 3; ++i)
5867 Env env(*
this, features);
5868 prep(env, rates.first, rates.second);
5870 if (i == 0 || i == 2)
5876 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5877 env(pay(carol, bob, USD(100)),
5884 BEAST_EXPECT(amm->expectBalances(
5885 USD(1'000), ETH(1'000), amm->tokens()));
5888 q[i] = Quality(Amounts{
5889 ETH(2'000) - env.
balance(carol, ETH),
5890 env.
balance(bob, USD) - USD(2'000)});
5893 BEAST_EXPECT(q[0] > q[1]);
5895 BEAST_EXPECT(q[0] == q[2]);
5902 for (
auto i = 0; i < 3; ++i)
5904 Env env(*
this, features);
5905 prep(env, rates.first, rates.second);
5907 if (i == 0 || i == 2)
5913 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5914 env(offer(alice, USD(400), ETH(400)));
5919 BEAST_EXPECT(amm->expectBalances(
5920 USD(1'000), ETH(1'000), amm->tokens()));
5922 if (i == 0 || i == 2)
5931 env, alice, 1, {Amounts{USD(400), ETH(400)}}));
5942 for (
auto i = 0; i < 3; ++i)
5944 Env env(*
this, features);
5945 prep(env, rates.first, rates.second);
5947 if (i == 0 || i == 2)
5953 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5954 env(pay(carol, bob, USD(100)),
5961 BEAST_EXPECT(!amm->expectBalances(
5962 USD(1'000), ETH(1'000), amm->tokens()));
5964 if (i == 2 && !features[fixAMMv1_1])
5966 if (rates.first == 1.5)
5968 if (!features[fixAMMv1_1])
5976 UINT64_C(378'6327949540823),
5980 UINT64_C(283'9745962155617),
5990 UINT64_C(378'6327949540813),
5994 UINT64_C(283'974596215561),
5999 if (!features[fixAMMv1_1])
6007 UINT64_C(325'299461620749),
6011 UINT64_C(243'9745962155617),
6021 UINT64_C(325'299461620748),
6025 UINT64_C(243'974596215561),
6031 if (rates.first == 1.5)
6039 ETH, UINT64_C(378'6327949540812), -13},
6042 UINT64_C(283'9745962155609),
6053 ETH, UINT64_C(325'2994616207479), -13},
6056 UINT64_C(243'9745962155609),
6061 q[i] = Quality(Amounts{
6062 ETH(2'000) - env.
balance(carol, ETH),
6063 env.
balance(bob, USD) - USD(2'000)});
6066 BEAST_EXPECT(q[1] > q[0]);
6068 BEAST_EXPECT(q[2] > q[1]);
6072 for (
auto i = 0; i < 3; ++i)
6074 Env env(*
this, features);
6075 prep(env, rates.first, rates.second);
6077 if (i == 0 || i == 2)
6083 amm.emplace(env, ed, USD(1'000), ETH(1'000));
6084 env(offer(alice, USD(250), ETH(400)));
6089 BEAST_EXPECT(!amm->expectBalances(
6090 USD(1'000), ETH(1'000), amm->tokens()));
6096 if (rates.first == 1.5)
6098 if (!features[fixAMMv1_1])
6101 env, ed, 1, {{Amounts{ETH(400), USD(250)}}}));
6108 USD, UINT64_C(40'5694150420947), -13},
6110 ETH, UINT64_C(64'91106406735152), -14},
6124 ETH, UINT64_C(335'0889359326475), -13},
6126 USD, UINT64_C(209'4305849579047), -13},
6133 if (!features[fixAMMv1_1])
6142 ETH, UINT64_C(335'0889359326485), -13},
6144 USD, UINT64_C(209'4305849579053), -13},
6157 ETH, UINT64_C(335'0889359326475), -13},
6159 USD, UINT64_C(209'4305849579047), -13},
6185 for (
auto i = 0; i < 3; ++i)
6187 Env env(*
this, features);
6188 prep(env, rates.first, rates.second);
6191 if (i == 0 || i == 2)
6199 amm.emplace(env, ed, ETH(1'000), USD(1'000));
6201 env(pay(carol, bob, USD(100)),
6209 if (i == 2 && !features[fixAMMv1_1])
6211 if (rates.first == 1.5)
6214 BEAST_EXPECT(amm->expectBalances(
6215 STAmount{ETH, UINT64_C(1'176'66038955758), -11},
6221 BEAST_EXPECT(amm->expectBalances(
6223 ETH, UINT64_C(1'179'540094339627), -12},
6224 STAmount{USD, UINT64_C(847'7880529867501), -13},
6233 UINT64_C(343'3179205198749),
6237 UINT64_C(343'3179205198749),
6243 UINT64_C(362'2119470132499),
6247 UINT64_C(362'2119470132499),
6254 if (rates.first == 1.5)
6257 BEAST_EXPECT(amm->expectBalances(
6259 ETH, UINT64_C(1'176'660389557593), -12},
6265 BEAST_EXPECT(amm->expectBalances(
6266 STAmount{ETH, UINT64_C(1'179'54009433964), -11},
6267 STAmount{USD, UINT64_C(847'7880529867501), -13},
6276 UINT64_C(343'3179205198749),
6280 UINT64_C(343'3179205198749),
6286 UINT64_C(362'2119470132499),
6290 UINT64_C(362'2119470132499),
6295 q[i] = Quality(Amounts{
6296 ETH(2'000) - env.
balance(carol, ETH),
6297 env.
balance(bob, USD) - USD(2'000)});
6299 BEAST_EXPECT(q[1] > q[0]);
6300 BEAST_EXPECT(q[2] > q[0] && q[2] < q[1]);
6408 testcase(
"Fix changeSpotPriceQuality");
6409 using namespace jtx;
6414 SucceedShouldSucceedResize,
6426 auto const xrpIouAmounts10_100 =
6428 auto const iouXrpAmounts10_100 =
6433 {
"0.001519763260828713",
"1558701", Quality{5414253689393440221}, 1000, FailShouldSucceed},
6434 {
"0.01099814367603737",
"1892611", Quality{5482264816516900274}, 1000, FailShouldSucceed},
6435 {
"0.78",
"796599", Quality{5630392334958379008}, 1000, FailShouldSucceed},
6436 {
"105439.2955578965",
"49398693", Quality{5910869983721805038}, 400, FailShouldSucceed},
6437 {
"12408293.23445213",
"4340810521", Quality{5911611095910090752}, 997, FailShouldSucceed},
6438 {
"1892611",
"0.01099814367603737", Quality{6703103457950430139}, 1000, FailShouldSucceed},
6439 {
"423028.8508101858",
"3392804520", Quality{5837920340654162816}, 600, FailShouldSucceed},
6440 {
"44565388.41001027",
"73890647", Quality{6058976634606450001}, 1000, FailShouldSucceed},
6441 {
"66831.68494832662",
"16", Quality{6346111134641742975}, 0, FailShouldSucceed},
6442 {
"675.9287302203422",
"1242632304", Quality{5625960929244093294}, 300, FailShouldSucceed},
6443 {
"7047.112186735699",
"1649845866", Quality{5696855348026306945}, 504, FailShouldSucceed},
6444 {
"840236.4402981238",
"47419053", Quality{5982561601648018688}, 499, FailShouldSucceed},
6445 {
"992715.618909774",
"189445631733", Quality{5697835648288106944}, 815, SucceedShouldSucceedResize},
6446 {
"504636667521",
"185545883.9506651", Quality{6343802275337659280}, 503, SucceedShouldSucceedResize},
6447 {
"992706.7218636649",
"189447316000", Quality{5697835648288106944}, 797, SucceedShouldSucceedResize},
6448 {
"1.068737911388205",
"127860278877", Quality{5268604356368739396}, 293, SucceedShouldSucceedResize},
6449 {
"17932506.56880419",
"189308.6043676173", Quality{6206460598195440068}, 311, SucceedShouldSucceedResize},
6450 {
"1.066379294658174",
"128042251493", Quality{5268559341368739328}, 270, SucceedShouldSucceedResize},
6451 {
"350131413924",
"1576879.110907892", Quality{6487411636539049449}, 650,
Fail},
6452 {
"422093460",
"2.731797662057464", Quality{6702911108534394924}, 1000,
Fail},
6453 {
"76128132223",
"367172.7148422662", Quality{6487263463413514240}, 548,
Fail},
6454 {
"132701839250",
"280703770.7695443", Quality{6273750681188885075}, 562,
Fail},
6455 {
"994165.7604612011",
"189551302411", Quality{5697835592690668727}, 815,
Fail},
6456 {
"45053.33303227917",
"86612695359", Quality{5625695218943638190}, 500,
Fail},
6457 {
"199649.077043865",
"14017933007", Quality{5766034667318524880}, 324,
Fail},
6458 {
"27751824831.70903",
"78896950", Quality{6272538159621630432}, 500,
Fail},
6459 {
"225.3731275781907",
"156431793648", Quality{5477818047604078924}, 989,
Fail},
6460 {
"199649.077043865",
"14017933007", Quality{5766036094462806309}, 324,
Fail},
6461 {
"3.590272027140361",
"20677643641", Quality{5406056147042156356}, 808,
Fail},
6462 {
"1.070884664490231",
"127604712776", Quality{5268620608623825741}, 293,
Fail},
6463 {
"3272.448829820197",
"6275124076", Quality{5625710328924117902}, 81,
Fail},
6464 {
"0.009059512633902926",
"7994028", Quality{5477511954775533172}, 1000,
Fail},
6465 {
"1",
"1.0", Quality{0}, 100,
Fail},
6466 {
"1.0",
"1", Quality{0}, 100,
Fail},
6467 {
"10",
"10.0", Quality{xrpIouAmounts10_100}, 100,
Fail},
6468 {
"10.0",
"10", Quality{iouXrpAmounts10_100}, 100,
Fail},
6469 {
"69864389131",
"287631.4543025075", Quality{6487623473313516078}, 451, Succeed},
6470 {
"4328342973",
"12453825.99247381", Quality{6272522264364865181}, 997, Succeed},
6471 {
"32347017",
"7003.93031579449", Quality{6347261126087916670}, 1000, Succeed},
6472 {
"61697206161",
"36631.4583206413", Quality{6558965195382476659}, 500, Succeed},
6473 {
"1654524979",
"7028.659825511603", Quality{6487551345110052981}, 504, Succeed},
6474 {
"88621.22277293179",
"5128418948", Quality{5766347291552869205}, 380, Succeed},
6475 {
"1892611",
"0.01099814367603737", Quality{6703102780512015436}, 1000, Succeed},
6476 {
"4542.639373338766",
"24554809", Quality{5838994982188783710}, 0, Succeed},
6477 {
"5132932546",
"88542.99750172683", Quality{6419203342950054537}, 380, Succeed},
6478 {
"78929964.1549083",
"1506494795", Quality{5986890029845558688}, 589, Succeed},
6479 {
"10096561906",
"44727.72453735605", Quality{6487455290284644551}, 250, Succeed},
6480 {
"5092.219565514988",
"8768257694", Quality{5626349534958379008}, 503, Succeed},
6481 {
"1819778294",
"8305.084302902864", Quality{6487429398998540860}, 415, Succeed},
6482 {
"6970462.633911943",
"57359281", Quality{6054087899185946624}, 850, Succeed},
6483 {
"3983448845",
"2347.543644281467", Quality{6558965195382476659}, 856, Succeed},
6487 {
"771493171",
"1.243473020567508", Quality{6707566798038544272}, 100, SucceedShouldFail},
6491 boost::regex rx(
"^\\d+$");
6492 boost::smatch match;
6496 auto rules = env.
current()->rules();
6498 for (
auto const& t : tests)
6508 auto const poolInIsXRP =
6510 auto const poolOutIsXRP =
6512 assert(!(poolInIsXRP && poolOutIsXRP));
6513 auto const poolIn = getPool(
std::get<0>(t), poolInIsXRP);
6514 auto const poolOut = getPool(
std::get<1>(t), poolOutIsXRP);
6518 Amounts{poolIn, poolOut},
6525 if (status == SucceedShouldSucceedResize)
6527 if (!features[fixAMMv1_1])
6528 BEAST_EXPECT(Quality{*amounts} < quality);
6530 BEAST_EXPECT(Quality{*amounts} >= quality);
6532 else if (status == Succeed)
6534 if (!features[fixAMMv1_1])
6536 Quality{*amounts} >= quality ||
6538 Quality{*amounts}, quality,
Number{1, -7}));
6540 BEAST_EXPECT(Quality{*amounts} >= quality);
6542 else if (status == FailShouldSucceed)
6545 features[fixAMMv1_1] &&
6546 Quality{*amounts} >= quality);
6548 else if (status == SucceedShouldFail)
6551 !features[fixAMMv1_1] &&
6552 Quality{*amounts} < quality &&
6554 Quality{*amounts}, quality,
Number{1, -7}));
6563 if (status ==
Fail && quality != Quality{0})
6565 auto tinyOffer = [&]() {
6572 Amounts{poolIn, poolOut},
6576 else if (
isXRP(poolOut))
6581 Amounts{poolIn, poolOut},
6586 auto const takerPays = toAmount<STAmount>(
6591 Amounts{poolIn, poolOut}, takerPays, tfee)};
6593 BEAST_EXPECT(Quality(tinyOffer) < quality);
6595 else if (status == FailShouldSucceed)
6597 BEAST_EXPECT(!features[fixAMMv1_1]);
6599 else if (status == SucceedShouldFail)
6601 BEAST_EXPECT(features[fixAMMv1_1]);
6608 !strcmp(e.
what(),
"changeSpotPriceQuality failed"));
6610 !features[fixAMMv1_1] && status == FailShouldSucceed);
6619 BEAST_EXPECT(!res.has_value());
6685 using namespace jtx;
6691 Account const gatehub{
"gatehub"};
6692 Account const bitstamp{
"bitstamp"};
6693 Account const trader{
"trader"};
6694 auto const usdGH = gatehub[
"USD"];
6695 auto const btcGH = gatehub[
"BTC"];
6696 auto const usdBIT = bitstamp[
"USD"];
6700 char const* testCase;
6701 double const poolUsdBIT;
6702 double const poolUsdGH;
6715 double const offer1BtcGH = 0.1;
6716 double const offer2BtcGH = 0.1;
6717 double const offer2UsdGH = 1;
6718 double const rateBIT = 0.0;
6719 double const rateGH = 0.0;
6724 for (
auto const& input : {
6726 .testCase =
"Test Fix Overflow Offer",
6729 .sendMaxUsdBIT{usdBIT(50)},
6730 .sendUsdGH{usdGH,
uint64_t(272'455089820359), -12},
6733 .failUsdBIT{usdBIT,
uint64_t(46'47826086956522), -14},
6734 .failUsdBITr{usdBIT,
uint64_t(46'47826086956521), -14},
6735 .goodUsdGH{usdGH,
uint64_t(96'7543114220382), -13},
6736 .goodUsdGHr{usdGH,
uint64_t(96'7543114222965), -13},
6737 .goodUsdBIT{usdBIT,
uint64_t(8'464739069120721), -15},
6738 .goodUsdBITr{usdBIT,
uint64_t(8'464739069098152), -15},
6739 .lpTokenBalance = {28'61817604250837, -14},
6740 .lpTokenBalanceAlt =
IOUAmount{28'61817604250836, -14},
6748 .testCase =
"Overflow test {1, 100, 0.111}",
6751 .sendMaxUsdBIT{usdBIT(0.111)},
6752 .sendUsdGH{usdGH, 100},
6755 .failUsdBIT{usdBIT,
uint64_t(1'111), -3},
6756 .failUsdBITr{usdBIT,
uint64_t(1'111), -3},
6757 .goodUsdGH{usdGH,
uint64_t(90'04347888284115), -14},
6758 .goodUsdGHr{usdGH,
uint64_t(90'04347888284201), -14},
6759 .goodUsdBIT{usdBIT,
uint64_t(1'111), -3},
6760 .goodUsdBITr{usdBIT,
uint64_t(1'111), -3},
6761 .lpTokenBalance{10, 0},
6762 .offer1BtcGH = 1e-5,
6764 .offer2UsdGH = 1e-5,
6769 .testCase =
"Overflow test {1, 100, 1.00}",
6772 .sendMaxUsdBIT{usdBIT(1.00)},
6773 .sendUsdGH{usdGH, 100},
6776 .failUsdBIT{usdBIT,
uint64_t(2), 0},
6777 .failUsdBITr{usdBIT,
uint64_t(2), 0},
6778 .goodUsdGH{usdGH,
uint64_t(52'94379354424079), -14},
6779 .goodUsdGHr{usdGH,
uint64_t(52'94379354424135), -14},
6780 .goodUsdBIT{usdBIT,
uint64_t(2), 0},
6781 .goodUsdBITr{usdBIT,
uint64_t(2), 0},
6782 .lpTokenBalance{10, 0},
6783 .offer1BtcGH = 1e-5,
6785 .offer2UsdGH = 1e-5,
6790 .testCase =
"Overflow test {1, 100, 4.6432}",
6793 .sendMaxUsdBIT{usdBIT(4.6432)},
6794 .sendUsdGH{usdGH, 100},
6797 .failUsdBIT{usdBIT,
uint64_t(5'6432), -4},
6798 .failUsdBITr{usdBIT,
uint64_t(5'6432), -4},
6799 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6800 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6801 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
6802 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
6803 .lpTokenBalance{10, 0},
6804 .offer1BtcGH = 1e-5,
6806 .offer2UsdGH = 1e-5,
6811 .testCase =
"Overflow test {1, 100, 10}",
6814 .sendMaxUsdBIT{usdBIT(10)},
6815 .sendUsdGH{usdGH, 100},
6818 .failUsdBIT{usdBIT,
uint64_t(11), 0},
6819 .failUsdBITr{usdBIT,
uint64_t(11), 0},
6820 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6821 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6822 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
6823 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
6824 .lpTokenBalance{10, 0},
6825 .offer1BtcGH = 1e-5,
6827 .offer2UsdGH = 1e-5,
6832 .testCase =
"Overflow test {50, 100, 5.55}",
6835 .sendMaxUsdBIT{usdBIT(5.55)},
6836 .sendUsdGH{usdGH, 100},
6839 .failUsdBIT{usdBIT,
uint64_t(55'55), -2},
6840 .failUsdBITr{usdBIT,
uint64_t(55'55), -2},
6841 .goodUsdGH{usdGH,
uint64_t(90'04347888284113), -14},
6842 .goodUsdGHr{usdGH,
uint64_t(90'0434788828413), -13},
6843 .goodUsdBIT{usdBIT,
uint64_t(55'55), -2},
6844 .goodUsdBITr{usdBIT,
uint64_t(55'55), -2},
6845 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
6846 .offer1BtcGH = 1e-5,
6848 .offer2UsdGH = 1e-5,
6853 .testCase =
"Overflow test {50, 100, 50.00}",
6856 .sendMaxUsdBIT{usdBIT(50.00)},
6857 .sendUsdGH{usdGH, 100},
6858 .failUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
6859 .failUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
6860 .failUsdBIT{usdBIT,
uint64_t(100), 0},
6861 .failUsdBITr{usdBIT,
uint64_t(100), 0},
6862 .goodUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
6863 .goodUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
6864 .goodUsdBIT{usdBIT,
uint64_t(100), 0},
6865 .goodUsdBITr{usdBIT,
uint64_t(100), 0},
6866 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
6867 .offer1BtcGH = 1e-5,
6869 .offer2UsdGH = 1e-5,
6874 .testCase =
"Overflow test {50, 100, 232.16}",
6877 .sendMaxUsdBIT{usdBIT(232.16)},
6878 .sendUsdGH{usdGH, 100},
6881 .failUsdBIT{usdBIT,
uint64_t(282'16), -2},
6882 .failUsdBITr{usdBIT,
uint64_t(282'16), -2},
6883 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6884 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6885 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
6886 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
6887 .lpTokenBalance{70'71067811865475, -14},
6888 .offer1BtcGH = 1e-5,
6890 .offer2UsdGH = 1e-5,
6895 .testCase =
"Overflow test {50, 100, 500}",
6898 .sendMaxUsdBIT{usdBIT(500)},
6899 .sendUsdGH{usdGH, 100},
6902 .failUsdBIT{usdBIT,
uint64_t(550), 0},
6903 .failUsdBITr{usdBIT,
uint64_t(550), 0},
6904 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6905 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6906 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
6907 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
6908 .lpTokenBalance{70'71067811865475, -14},
6909 .offer1BtcGH = 1e-5,
6911 .offer2UsdGH = 1e-5,
6917 testcase(input.testCase);
6918 for (
auto const& features :
6919 {all - fixAMMOverflowOffer - fixAMMv1_1 - fixAMMv1_3, all})
6923 env.
fund(
XRP(5'000), gatehub, bitstamp, trader);
6926 if (input.rateGH != 0.0)
6927 env(rate(gatehub, input.rateGH));
6928 if (input.rateBIT != 0.0)
6929 env(rate(bitstamp, input.rateBIT));
6931 env(trust(trader, usdGH(10'000'000)));
6932 env(trust(trader, usdBIT(10'000'000)));
6933 env(trust(trader, btcGH(10'000'000)));
6936 env(pay(gatehub, trader, usdGH(100'000)));
6937 env(pay(gatehub, trader, btcGH(100'000)));
6938 env(pay(bitstamp, trader, usdBIT(100'000)));
6944 usdGH(input.poolUsdGH),
6945 usdBIT(input.poolUsdBIT)};
6949 amm.getLPTokensBalance();
6951 env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
6954 btcGH(input.offer2BtcGH),
6955 usdGH(input.offer2UsdGH)));
6958 env(pay(trader, trader, input.sendUsdGH),
6960 path(~btcGH, ~usdGH),
6965 auto const failUsdGH =
6966 features[fixAMMv1_1] ? input.failUsdGHr : input.failUsdGH;
6967 auto const failUsdBIT =
6968 features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT;
6969 auto const goodUsdGH =
6970 features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH;
6971 auto const goodUsdBIT =
6972 features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT;
6973 auto const lpTokenBalance =
6974 env.
enabled(fixAMMv1_3) && input.lpTokenBalanceAlt
6975 ? *input.lpTokenBalanceAlt
6976 : input.lpTokenBalance;
6977 if (!features[fixAMMOverflowOffer])
6979 BEAST_EXPECT(amm.expectBalances(
6980 failUsdGH, failUsdBIT, lpTokenBalance));
6984 BEAST_EXPECT(amm.expectBalances(
6985 goodUsdGH, goodUsdBIT, lpTokenBalance));
6990 amm.getLPTokensBalance() == preSwapLPTokenBalance);
6994 Number const sqrtPoolProduct =
6995 root2(goodUsdGH * goodUsdBIT);
7006 (sqrtPoolProduct +
Number{1, -14} >=
7007 input.lpTokenBalance));