3014 using namespace jtx;
3021 [&](
AMM& ammAlice,
Env& env) {
3022 ammAlice.
deposit(carol, 1'000'000);
3023 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
3036 [&](
AMM& ammAlice,
Env& env) {
3037 ammAlice.
deposit(carol, 1'000'000);
3040 {.account = carol, .bidMin = 110, .bidMax = 110}));
3046 {.account = alice, .bidMin = 180, .bidMax = 200}));
3049 XRP(11'000), USD(11'000),
IOUAmount{10'999'814'5, -1}));
3058 [&](
AMM& ammAlice,
Env& env) {
3059 ammAlice.
deposit(carol, 1'000'000);
3061 env(ammAlice.
bid({.account = carol, .bidMin = 110}));
3064 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
3065 ammAlice.
deposit(bob, 1'000'000);
3067 env(ammAlice.
bid({.account = bob}));
3078 env(ammAlice.
bid({.account = carol, .bidMax = 600}));
3092 {.account = carol, .bidMin = 100, .bidMax = 600}));
3103 [&](
AMM& ammAlice,
Env& env) {
3104 ammAlice.
deposit(carol, 1'000'000);
3106 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
3107 ammAlice.
deposit(bob, 1'000'000);
3108 if (!features[fixAMMv1_3])
3118 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
3122 env(ammAlice.
bid({.account = bob}));
3128 env(ammAlice.
bid({.account = carol}));
3134 env(ammAlice.
bid({.account = bob}));
3143 env(ammAlice.
bid({.account = carol, .bidMin = 110})).
close();
3147 if (!features[fixAMMv1_3])
3168 [&](
AMM& ammAlice,
Env& env) {
3171 fund(env, gw, {bob, dan, ed}, {USD(20'000)}, Fund::Acct);
3172 ammAlice.
deposit(bob, 1'000'000);
3173 ammAlice.
deposit(ed, 1'000'000);
3174 ammAlice.
deposit(carol, 500'000);
3175 ammAlice.
deposit(dan, 500'000);
3180 .authAccounts = {bob, ed},
3182 auto const slotPrice =
IOUAmount{5'200};
3183 ammTokens -= slotPrice;
3185 if (!features[fixAMMv1_3])
3187 XRP(13'000), USD(13'000), ammTokens));
3190 XRPAmount{13'000'000'003}, USD(13'000), ammTokens));
3192 for (
int i = 0; i < 10; ++i)
3194 auto tokens = ammAlice.
deposit(carol, USD(100));
3195 ammAlice.
withdraw(carol, tokens, USD(0));
3196 tokens = ammAlice.
deposit(bob, USD(100));
3197 ammAlice.
withdraw(bob, tokens, USD(0));
3198 tokens = ammAlice.
deposit(ed, USD(100));
3199 ammAlice.
withdraw(ed, tokens, USD(0));
3202 if (!features[fixAMMv1_1])
3206 STAmount(USD, UINT64_C(29'499'00572620545), -11));
3209 STAmount(USD, UINT64_C(18'999'00572616195), -11));
3212 STAmount(USD, UINT64_C(18'999'00572611841), -11));
3216 STAmount(USD, UINT64_C(13'002'98282151419), -11),
3223 STAmount(USD, UINT64_C(29'499'00572620544), -11));
3226 STAmount(USD, UINT64_C(18'999'00572616194), -11));
3229 STAmount(USD, UINT64_C(18'999'0057261184), -10));
3231 if (!features[fixAMMv1_3])
3234 STAmount(USD, UINT64_C(13'002'98282151422), -11),
3239 STAmount(USD, UINT64_C(13'002'98282151422), -11),
3244 for (
int i = 0; i < 10; ++i)
3246 auto const tokens = ammAlice.
deposit(dan, USD(100));
3247 ammAlice.
withdraw(dan, tokens, USD(0));
3252 if (!features[fixAMMv1_1])
3256 STAmount(USD, UINT64_C(19'490'056722744), -9));
3260 STAmount{USD, UINT64_C(13'012'92609877019), -11},
3263 ammAlice.
deposit(carol, USD(100));
3267 STAmount{USD, UINT64_C(13'112'92609877019), -11},
3269 env(pay(carol, bob, USD(100)),
3277 STAmount{USD, UINT64_C(13'012'92609877019), -11},
3282 if (!features[fixAMMv1_3])
3285 STAmount(USD, UINT64_C(19'490'05672274399), -11));
3289 STAmount(USD, UINT64_C(19'490'05672274398), -11));
3291 if (!features[fixAMMv1_3])
3294 STAmount{USD, UINT64_C(13'012'92609877023), -11},
3299 STAmount{USD, UINT64_C(13'012'92609877024), -11},
3302 ammAlice.
deposit(carol, USD(100));
3304 if (!features[fixAMMv1_3])
3307 STAmount{USD, UINT64_C(13'112'92609877023), -11},
3312 STAmount{USD, UINT64_C(13'112'92609877024), -11},
3314 env(pay(carol, bob, USD(100)),
3320 if (!features[fixAMMv1_3])
3323 STAmount{USD, UINT64_C(13'012'92609877023), -11},
3328 STAmount{USD, UINT64_C(13'012'92609877024), -11},
3337 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
3341 STAmount{USD, UINT64_C(13'114'03663047264), -11},
3344 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
3348 STAmount{USD, UINT64_C(13'114'03663047269), -11},
3355 STAmount{USD, UINT64_C(13'114'03663044937), -11},
3362 if (!features[fixAMMv1_1])
3365 STAmount(USD, UINT64_C(29'399'00572620545), -11));
3366 else if (!features[fixAMMv1_3])
3369 STAmount(USD, UINT64_C(29'399'00572620544), -11));
3371 for (
int i = 0; i < 10; ++i)
3373 auto const tokens = ammAlice.
deposit(carol, USD(100));
3374 ammAlice.
withdraw(carol, tokens, USD(0));
3378 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
3382 STAmount(USD, UINT64_C(29'389'06197177128), -11));
3385 STAmount{USD, UINT64_C(13'123'98038490681), -11},
3388 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
3392 STAmount(USD, UINT64_C(29'389'06197177124), -11));
3395 STAmount{USD, UINT64_C(13'123'98038490689), -11},
3402 STAmount(USD, UINT64_C(29'389'06197177129), -11));
3405 STAmount{USD, UINT64_C(13'123'98038488352), -11},
3413 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
3417 STAmount{USD, UINT64_C(13'023'98038490681), -11},
3420 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
3424 STAmount{USD, UINT64_C(13'023'98038490689), -11},
3431 STAmount{USD, UINT64_C(13'023'98038488352), -11},
3442 [&](AMM& ammAlice, Env& env) {
3445 Number{STAmount::cMinValue, STAmount::cMinOffset};
3447 {.account = alice, .bidMin = IOUAmount{tiny}}));
3450 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny}));
3452 BEAST_EXPECT(ammAlice.expectBalances(
3453 XRP(10'000), USD(10'000), ammAlice.tokens()));
3458 IOUAmount{STAmount::cMinValue, STAmount::cMinOffset},
3461 BEAST_EXPECT(ammAlice.expectAuctionSlot(
3462 0, 0, IOUAmount{tiny * Number{105, -2}}));
3465 BEAST_EXPECT(ammAlice.expectBalances(
3466 XRP(10'000), USD(10'000), ammAlice.tokens()));
3475 [&](AMM& ammAlice, Env& env) {
3478 .bidMin = IOUAmount{100},
3479 .authAccounts = {carol},
3481 BEAST_EXPECT(ammAlice.expectAuctionSlot({carol}));
3482 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{100}}));
3483 BEAST_EXPECT(ammAlice.expectAuctionSlot({}));
3486 fund(env, {bob, dan},
XRP(1'000));
3489 .bidMin = IOUAmount{100},
3490 .authAccounts = {bob, dan},
3492 BEAST_EXPECT(ammAlice.expectAuctionSlot({bob, dan}));
3501 Env env(*
this, features);
3502 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
3503 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
3504 auto const lpIssue =
amm.lptIssue();
3505 env.trust(STAmount{lpIssue, 500}, alice);
3506 env.trust(STAmount{lpIssue, 50}, bob);
3507 env(
pay(gw, alice, STAmount{lpIssue, 500}));
3508 env(
pay(gw, bob, STAmount{lpIssue, 50}));
3510 env(
amm.bid({.account = alice, .bidMin = 500}));
3511 BEAST_EXPECT(
amm.expectAuctionSlot(100, 0, IOUAmount{500}));
3512 BEAST_EXPECT(
expectHolding(env, alice, STAmount{lpIssue, 0}));
3515 env(
pay(alice, bob, USD(10)), path(~USD), sendmax(
XRP(11)));
3516 BEAST_EXPECT(
amm.expectBalances(
3517 XRPAmount{1'010'010'011},
3519 IOUAmount{1'004'487'562112089, -9}));
3521 env(
pay(bob, alice,
XRP(10)), path(~XRP), sendmax(USD(11)));
3522 if (!features[fixAMMv1_1])
3524 BEAST_EXPECT(
amm.expectBalances(
3525 XRPAmount{1'000'010'011},
3526 STAmount{USD, UINT64_C(1'010'10090898081), -11},
3527 IOUAmount{1'004'487'562112089, -9}));
3531 BEAST_EXPECT(
amm.expectBalances(
3532 XRPAmount{1'000'010'011},
3533 STAmount{USD, UINT64_C(1'010'100908980811), -12},
3534 IOUAmount{1'004'487'562112089, -9}));
3540 Env env(*
this, features);
3541 auto const baseFee = env.current()->fees().base;
3543 fund(env, gw, {alice, bob},
XRP(2'000), {USD(2'000)});
3544 AMM amm(env, gw,
XRP(1'000), USD(1'010),
false, 1'000);
3548 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3549 env.app().config().features.erase(featureAMM);
3550 PreflightContext pfctx(
3553 env.current()->rules(),
3556 auto pf = Transactor::invokePreflight<AMMBid>(pfctx);
3557 BEAST_EXPECT(pf == temDISABLED);
3558 env.app().config().features.insert(featureAMM);
3562 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3563 jtx.jv[
"TxnSignature"] =
"deadbeef";
3564 jtx.stx = env.ust(jtx);
3565 PreflightContext pfctx(
3568 env.current()->rules(),
3571 auto pf = Transactor::invokePreflight<AMMBid>(pfctx);
3572 BEAST_EXPECT(pf != tesSUCCESS);
3576 auto jtx = env.jt(tx, seq(1), fee(baseFee));
3577 jtx.jv[
"Asset2"][
"currency"] =
"XRP";
3578 jtx.jv[
"Asset2"].removeMember(
"issuer");
3579 jtx.stx = env.ust(jtx);
3580 PreflightContext pfctx(
3583 env.current()->rules(),
3586 auto pf = Transactor::invokePreflight<AMMBid>(pfctx);
3587 BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
3758 testcase(
"Basic Payment");
3759 using namespace jtx;
3764 [&](
AMM& ammAlice,
Env& env) {
3765 env.
fund(jtx::XRP(30'000), bob);
3767 env(pay(bob, carol, USD(100)),
3773 XRP(10'100), USD(10'000), ammAlice.
tokens()));
3778 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
3780 {{
XRP(10'000), USD(10'100)}},
3787 [&](
AMM& ammAlice,
Env& env) {
3788 env.
fund(jtx::XRP(30'000), bob);
3790 env(pay(bob, carol, USD(100)),
sendmax(
XRP(100)));
3793 XRP(10'100), USD(10'000), ammAlice.
tokens()));
3798 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
3800 {{
XRP(10'000), USD(10'100)}},
3808 [&](
AMM& ammAlice,
Env& env) {
3809 env.
fund(jtx::XRP(30'000), bob);
3814 XRP(10'100), USD(10'000), ammAlice.
tokens()));
3819 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
3821 {{
XRP(10'000), USD(10'100)}},
3828 [&](
AMM& ammAlice,
Env& env) {
3829 env.
fund(jtx::XRP(30'000), bob);
3833 env(pay(bob, carol, USD(100)),
3840 XRP(10'010), USD(10'000), ammAlice.
tokens()));
3846 env, bob,
XRP(30'000) -
XRP(10) -
txfee(env, 1)));
3850 env(pay(bob, carol, USD(100)),
3858 {{
XRP(10'000), USD(10'010)}},
3865 [&](
AMM& ammAlice,
Env& env) {
3868 env.
fund(jtx::XRP(30'000), bob);
3873 env(pay(bob, carol, USD(100)),
3880 XRP(10'010), USD(10'000), ammAlice.
tokens()));
3885 STAmount{USD, UINT64_C(30'009'09090909091), -11}));
3887 env, bob,
XRP(30'000) -
XRP(10) -
txfee(env, 1)));
3889 {{
XRP(10'000), USD(10'010)}},
3896 [&](
AMM& ammAlice,
Env& env) {
3897 env.
fund(jtx::XRP(30'000), bob);
3899 env(pay(bob, carol, USD(100)),
3905 {{
XRP(10'000), USD(10'000)}},
3915 Env env(*
this, features);
3917 env, gw, {alice, carol}, {USD(30'000), EUR(30'000)}, Fund::All);
3921 auto ammEUR_XRP =
AMM(env, alice,
XRP(10'000), EUR(10'000));
3922 auto ammUSD_EUR =
AMM(env, alice, EUR(10'000), USD(10'000));
3925 env(pay(bob, carol, USD(100)),
3930 BEAST_EXPECT(ammEUR_XRP.expectBalances(
3932 STAmount(EUR, UINT64_C(9'970'007498125468), -12),
3933 ammEUR_XRP.tokens()));
3934 if (!features[fixAMMv1_1])
3936 BEAST_EXPECT(ammUSD_EUR.expectBalances(
3937 STAmount(USD, UINT64_C(9'970'097277662122), -12),
3938 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
3939 ammUSD_EUR.tokens()));
3942 Amounts
const expectedAmounts =
3943 env.
closed()->rules().enabled(fixReducedOffersV2)
3944 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233787816), -14)}
3947 STAmount(USD, UINT64_C(29'90272233787818), -14)};
3949 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
3953 BEAST_EXPECT(ammUSD_EUR.expectBalances(
3954 STAmount(USD, UINT64_C(9'970'097277662172), -12),
3955 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
3956 ammUSD_EUR.tokens()));
3959 Amounts
const expectedAmounts =
3960 env.
closed()->rules().enabled(fixReducedOffersV2)
3961 ? Amounts{
XRPAmount(30'201'749),
STAmount(USD, UINT64_C(29'90272233782839), -14)}
3964 STAmount(USD, UINT64_C(29'90272233782840), -14)};
3966 BEAST_EXPECT(
expectOffers(env, alice, 1, {{expectedAmounts}}));
3982 [&](
AMM& ammAlice,
Env& env) {
3985 env.
trust(EUR(2'000), alice);
3987 env(pay(gw, alice, EUR(1'000)));
3992 env(pay(bob, carol, USD(100)),
3999 STAmount(USD, UINT64_C(9'950'01249687578), -11),
4007 STAmount(EUR, UINT64_C(49'98750312422), -11)},
4009 STAmount(EUR, UINT64_C(49'98750312422), -11),
4010 STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
4015 STAmount{USD, UINT64_C(30'099'99999999999), -11}));
4032 [&](
AMM& ammAlice,
Env& env) {
4033 fund(env, gw, {bob}, {USD(100)}, Fund::Acct);
4037 env(pay(alice, carol, USD(200)),
4041 if (!features[fixAMMv1_1])
4044 XRP(10'100), USD(10'000), ammAlice.
tokens()));
4052 STAmount(USD, UINT64_C(10'000'00000000001), -11),
4057 STAmount(USD, UINT64_C(30'199'99999999999), -11)));
4065 ammCrtFee(env) -
txfee(env, 1)));
4068 {{
XRP(10'000), USD(10'100)}},
4077 Env env(*
this, features);
4078 fund(env, gw, {alice, bob, carol},
XRP(20'000), {USD(2'000)});
4082 AMM ammAlice(env, alice,
XRP(1'000), USD(1'050));
4083 env(pay(alice, carol, USD(200)),
4088 XRP(1'050), USD(1'000), ammAlice.
tokens()));
4095 [&](
AMM& ammAlice,
Env& env) {
4096 fund(env, gw, {bob}, {USD(1'000)}, Fund::Acct);
4098 env(offer(bob, USD(100),
XRP(100)));
4101 XRP(10'100), USD(10'000), ammAlice.
tokens()));
4106 env, bob,
XRP(30'000) -
XRP(100) -
txfee(env, 1)));
4109 {{
XRP(10'000), USD(10'100)}},
4117 [&](
AMM& ammAlice,
Env& env) {
4118 env(rate(gw, 1.25));
4124 env(offer(carol, EUR(100), GBP(100)));
4128 GBP(1'100), EUR(1'000), ammAlice.
tokens()));
4135 {{GBP(1'000), EUR(1'100)}},
4141 [&](
AMM& amm,
Env& env) {
4142 env(rate(gw, 1.001));
4144 env(offer(carol,
XRP(100), USD(55)));
4146 if (!features[fixAMMv1_1])
4156 amm.expectBalances(
XRP(1'000), USD(500), amm.tokens()));
4158 env, carol, 1, {{Amounts{
XRP(100), USD(55)}}}));
4169 BEAST_EXPECT(amm.expectBalances(
4171 STAmount{USD, UINT64_C(550'000000055), -9},
4180 STAmount{USD, 4'99999995, -8}}}}));
4184 STAmount(USD, UINT64_C(29'949'94999999494), -11));
4187 {{
XRP(1'000), USD(500)}},
4192 [&](
AMM& amm,
Env& env) {
4193 env(rate(gw, 1.001));
4195 env(offer(carol,
XRP(10), USD(5.5)));
4197 if (!features[fixAMMv1_1])
4199 BEAST_EXPECT(amm.expectBalances(
4201 STAmount{USD, UINT64_C(505'050505050505), -12},
4207 BEAST_EXPECT(amm.expectBalances(
4209 STAmount{USD, UINT64_C(505'0505050505051), -13},
4214 {{
XRP(1'000), USD(500)}},
4220 [&](
AMM& ammAlice,
Env& env) {
4227 {GBP(2'000), EUR(2'000)},
4229 env(rate(gw, 1.25));
4240 env(offer(carol, EUR(100), GBP(100)));
4242 if (!features[fixAMMv1_1])
4251 STAmount{GBP, UINT64_C(1'037'06583722133), -11},
4252 STAmount{EUR, UINT64_C(1'060'684828792831), -12},
4260 STAmount{EUR, UINT64_C(50'684828792831), -12},
4261 STAmount{GBP, UINT64_C(50'684828792831), -12}}}));
4273 STAmount{GBP, UINT64_C(29'941'16770347333), -11}));
4278 STAmount{EUR, UINT64_C(30'049'31517120716), -11}));
4290 STAmount{GBP, UINT64_C(1'060'684828792832), -12},
4291 STAmount{EUR, UINT64_C(1'037'06583722134), -11},
4299 STAmount{EUR, UINT64_C(27'06583722134028), -14},
4300 STAmount{GBP, UINT64_C(27'06583722134028), -14}}}));
4312 STAmount{GBP, UINT64_C(29'911'64396400896), -11}));
4317 STAmount{EUR, UINT64_C(30'072'93416277865), -11}));
4324 {{GBP(1'000), EUR(1'100)}},
4337 [&](
AMM& ammAlice,
Env& env) {
4338 fund(env, gw, {bob}, {GBP(200), EUR(200)}, Fund::Acct);
4339 env(rate(gw, 1.25));
4341 env(pay(bob, carol, EUR(100)),
4347 GBP(1'100), EUR(1'000), ammAlice.
tokens()));
4351 {{GBP(1'000), EUR(1'100)}},
4368 [&](
AMM& ammAlice,
Env& env) {
4371 auto const CAN = gw[
"CAN"];
4372 fund(env, gw, {dan}, {CAN(200), GBP(200)}, Fund::Acct);
4373 fund(env, gw, {ed}, {EUR(200), USD(200)}, Fund::Acct);
4374 fund(env, gw, {bob}, {CAN(195.3125)}, Fund::Acct);
4375 env(trust(carol, USD(100)));
4376 env(rate(gw, 1.25));
4378 env(offer(dan, CAN(200), GBP(200)));
4379 env(offer(ed, EUR(200), USD(200)));
4381 env(pay(bob, carol, USD(100)),
4382 path(~GBP, ~EUR, ~USD),
4387 BEAST_EXPECT(
expectHolding(env, dan, CAN(356.25), GBP(43.75)));
4389 GBP(10'125), EUR(10'000), ammAlice.
tokens()));
4393 {{GBP(10'000), EUR(10'125)}},
4400 [&](
AMM& ammAlice,
Env& env) {
4401 env(pay(alice, carol, USD(99.99)),
4406 env(pay(alice, carol, USD(100)),
4411 env(pay(alice, carol,
XRP(100)),
4422 {{
XRP(100), USD(100)}},
4429 Env env(*
this, features);
4430 auto const ETH = gw[
"ETH"];
4436 {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
4437 fund(env, gw, {carol, bob},
XRP(1'000), {USD(200)}, Fund::Acct);
4438 AMM xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
4439 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
4440 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
4441 AMM xrp_usd(env, alice,
XRP(10'150), USD(10'200));
4442 AMM xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
4443 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
4444 AMM eur_usd(env, alice, EUR(10'100), USD(10'000));
4445 env(pay(bob, carol, USD(100)),
4446 path(~EUR, ~BTC, ~USD),
4448 path(~ETH, ~EUR, ~USD),
4450 if (!features[fixAMMv1_1])
4456 STAmount{ETH, UINT64_C(10'073'65779244494), -11},
4459 STAmount{ETH, UINT64_C(10'926'34220755506), -11},
4460 STAmount{EUR, UINT64_C(10'973'54232078752), -11},
4463 STAmount{EUR, UINT64_C(10'126'45767921248), -11},
4464 STAmount{USD, UINT64_C(9'973'93151712086), -11},
4470 STAmount{USD, UINT64_C(10'126'06848287914), -11},
4477 STAmount{ETH, UINT64_C(10'073'65779244461), -11},
4480 STAmount{ETH, UINT64_C(10'926'34220755539), -11},
4481 STAmount{EUR, UINT64_C(10'973'5423207872), -10},
4484 STAmount{EUR, UINT64_C(10'126'4576792128), -10},
4485 STAmount{USD, UINT64_C(9'973'93151712057), -11},
4491 STAmount{USD, UINT64_C(10'126'06848287943), -11},
4501 BEAST_EXPECT(xrp_eur.expectBalances(
4502 XRP(10'100), EUR(10'000), xrp_eur.tokens()));
4504 EUR(10'000), BTC(10'200), eur_btc.
tokens()));
4506 BTC(10'100), USD(10'000), btc_usd.
tokens()));
4513 Env env(*
this, features);
4514 auto const ETH = gw[
"ETH"];
4520 {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
4521 fund(env, gw, {carol, bob},
XRP(1000), {USD(200)}, Fund::Acct);
4522 AMM xrp_eur(env, alice,
XRP(10'100), EUR(10'000));
4523 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
4524 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
4525 AMM xrp_eth(env, alice,
XRP(10'000), ETH(10'100));
4526 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
4527 env(pay(bob, carol, USD(100)),
4528 path(~EUR, ~BTC, ~USD),
4529 path(~ETH, ~EUR, ~BTC, ~USD),
4531 if (!features[fixAMMv1_1])
4535 BEAST_EXPECT(xrp_eur.expectBalances(
4537 STAmount{EUR, UINT64_C(9'981'544436337968), -12},
4540 STAmount{EUR, UINT64_C(10'101'16096785173), -11},
4541 STAmount{BTC, UINT64_C(10'097'91426968066), -11},
4544 STAmount{BTC, UINT64_C(10'202'08573031934), -11},
4549 STAmount{ETH, UINT64_C(10'017'41072778012), -11},
4552 STAmount{ETH, UINT64_C(10'982'58927221988), -11},
4553 STAmount{EUR, UINT64_C(10'917'2945958103), -10},
4558 BEAST_EXPECT(xrp_eur.expectBalances(
4560 STAmount{EUR, UINT64_C(9'981'544436337923), -12},
4563 STAmount{EUR, UINT64_C(10'101'16096785188), -11},
4564 STAmount{BTC, UINT64_C(10'097'91426968059), -11},
4567 STAmount{BTC, UINT64_C(10'202'08573031941), -11},
4572 STAmount{ETH, UINT64_C(10'017'41072777996), -11},
4575 STAmount{ETH, UINT64_C(10'982'58927222004), -11},
4576 STAmount{EUR, UINT64_C(10'917'2945958102), -10},
4585 [&](
AMM& ammAlice,
Env& env) {
4587 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
4588 env(trust(alice, EUR(200)));
4589 for (
int i = 0; i < 30; ++i)
4590 env(offer(alice, EUR(1.0 + 0.01 * i),
XRP(1)));
4593 env(offer(alice, EUR(140),
XRP(100)));
4594 env(pay(bob, carol, USD(100)),
4598 if (!features[fixAMMv1_1])
4603 STAmount{USD, UINT64_C(9'970'089730807577), -12},
4608 STAmount{USD, UINT64_C(30'029'91026919241), -11}));
4614 STAmount{USD, UINT64_C(9'970'089730807827), -12},
4619 STAmount{USD, UINT64_C(30'029'91026919217), -11}));
4630 [&](
AMM& ammAlice,
Env& env) {
4632 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
4633 env(trust(alice, EUR(200)));
4634 for (
int i = 0; i < 29; ++i)
4635 env(offer(alice, EUR(1.0 + 0.01 * i),
XRP(1)));
4638 env(offer(alice, EUR(140),
XRP(100)));
4639 env(pay(bob, carol, USD(100)),
4645 if (!features[fixAMMv1_1])
4651 STAmount{USD, UINT64_C(30'099'99999999999), -11}));
4661 {{{
STAmount{EUR, UINT64_C(39'1858572), -7},
4672 Env env(*
this, features);
4673 fund(env, gw, {alice, carol, bob},
XRP(30'000), {USD(30'000)});
4674 env(offer(bob,
XRP(100), USD(100.001)));
4675 AMM ammAlice(env, alice,
XRP(10'000), USD(10'100));
4676 env(offer(carol, USD(100),
XRP(100)));
4677 if (!features[fixAMMv1_1])
4681 STAmount{USD, UINT64_C(10'049'92586949302), -11},
4688 STAmount{USD, UINT64_C(50'07513050698), -11}}}}));
4694 STAmount{USD, UINT64_C(10'049'92587049303), -11},
4701 STAmount{USD, UINT64_C(50'07512950697), -11}}}}));
4708 [&](
AMM& ammAlice,
Env& env) {
4712 env(pay(alice, carol, USD(1)),
5008 testcase(
"Trading Fee");
5009 using namespace jtx;
5013 [&](
AMM& ammAlice,
Env& env) {
5015 ammAlice.
deposit(carol, USD(3'000));
5021 ammAlice.
vote(alice, 1'000);
5025 ammAlice.
deposit(carol, USD(3'000));
5027 carol,
IOUAmount{994'981155689671, -12}));
5030 ammAlice.
vote(alice, 0);
5036 STAmount{USD, UINT64_C(29'994'96220068281), -11}));
5038 {{USD(1'000), EUR(1'000)}},
5046 [&](
AMM& ammAlice,
Env& env) {
5048 auto tokensFee = ammAlice.
deposit(
5052 ammAlice.
vote(alice, 0);
5054 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
5057 BEAST_EXPECT(tokensFee ==
IOUAmount(485'636'0611129, -7));
5058 BEAST_EXPECT(tokensNoFee ==
IOUAmount(487'644'85901109, -8));
5068 [&](
AMM& ammAlice,
Env& env) {
5070 auto const tokensFee = ammAlice.
deposit(
5074 ammAlice.
vote(alice, 0);
5076 auto const tokensNoFee = ammAlice.
deposit(carol, deposit);
5079 BEAST_EXPECT(tokensFee ==
IOUAmount(98'000'00000002, -8));
5080 BEAST_EXPECT(tokensNoFee ==
IOUAmount(98'475'81871545, -8));
5089 [&](
AMM& ammAlice,
Env& env) {
5091 ammAlice.
deposit(carol, USD(3'000));
5096 ammAlice.
vote(alice, 1'000);
5103 STAmount{USD, UINT64_C(29'994'97487437186), -11}));
5105 {{USD(1'000), EUR(1'000)}},
5112 [&](
AMM& ammAlice,
Env& env) {
5113 ammAlice.
deposit(carol, 1'000'000);
5114 auto const tokensFee = ammAlice.
withdraw(
5117 auto const balanceAfterWithdraw = [&]() {
5118 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
5119 return STAmount(USD, UINT64_C(30'443'43891402715), -11);
5120 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
5121 return STAmount(USD, UINT64_C(30'443'43891402714), -11);
5123 return STAmount(USD, UINT64_C(30'443'43891402713), -11);
5125 BEAST_EXPECT(env.
balance(carol, USD) == balanceAfterWithdraw);
5127 auto const deposit = balanceAfterWithdraw - USD(29'000);
5128 ammAlice.
deposit(carol, deposit);
5130 ammAlice.
vote(alice, 0);
5132 auto const tokensNoFee = ammAlice.
withdraw(carol, deposit);
5133 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
5136 STAmount(USD, UINT64_C(30'443'43891402717), -11));
5137 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
5140 STAmount(USD, UINT64_C(30'443'43891402716), -11));
5144 STAmount(USD, UINT64_C(30'443'43891402713), -11));
5147 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
5149 tokensNoFee ==
IOUAmount(746'579'80779913, -8));
5150 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
5152 tokensNoFee ==
IOUAmount(746'579'80779912, -8));
5155 tokensNoFee ==
IOUAmount(746'579'80779911, -8));
5156 BEAST_EXPECT(tokensFee ==
IOUAmount(750'588'23529411, -8));
5165 [&](
AMM& ammAlice,
Env& env) {
5171 {USD(1'000), EUR(1'000)},
5178 env(pay(carol, alice, EUR(10)),
5189 ammAlice.
vote(alice, 1'000);
5192 env(pay(bob, carol, USD(10)),
5199 env, bob,
STAmount{EUR, UINT64_C(989'8989898989899), -13}));
5204 STAmount{EUR, UINT64_C(1'010'10101010101), -11},
5207 {{USD(1'000), EUR(1'010)}},
5214 [&](
AMM& ammAlice,
Env& env) {
5216 env(offer(carol, EUR(10), USD(10)));
5221 env(offer(carol, USD(10), EUR(10)));
5224 ammAlice.
vote(alice, 500);
5226 env(offer(carol, EUR(10), USD(10)));
5233 STAmount{USD, UINT64_C(29'995'02512562814), -11}));
5237 STAmount{EUR, UINT64_C(30'004'97487437186), -11}));
5243 STAmount{EUR, UINT64_C(5'025125628140703), -15},
5244 STAmount{USD, UINT64_C(5'025125628140703), -15}}}}));
5245 if (!features[fixAMMv1_1])
5248 STAmount{USD, UINT64_C(1'004'974874371859), -12},
5249 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
5255 STAmount{USD, UINT64_C(1'004'97487437186), -11},
5256 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
5260 {{USD(1'000), EUR(1'010)}},
5270 Env env(*
this, features);
5275 {alice, bob, carol, ed},
5277 {USD(2'000), EUR(2'000)});
5278 env(offer(carol, EUR(5), USD(5)));
5279 AMM ammAlice(env, alice, USD(1'005), EUR(1'000));
5280 env(pay(bob, ed, USD(10)),
5285 if (!features[fixAMMv1_1])
5289 USD(1'000), EUR(1'005), ammAlice.
tokens()));
5294 env, bob,
STAmount(EUR, UINT64_C(1989'999999999999), -12)));
5297 STAmount(EUR, UINT64_C(1005'000000000001), -12),
5306 Env env(*
this, features);
5311 {alice, bob, carol, ed},
5313 {USD(2'000), EUR(2'000)});
5314 env(offer(carol, EUR(5), USD(5)));
5316 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 250);
5317 env(pay(bob, ed, USD(10)),
5322 if (!features[fixAMMv1_1])
5327 STAmount{EUR, UINT64_C(1'989'987453007618), -12}));
5330 STAmount{EUR, UINT64_C(1'005'012546992382), -12},
5338 STAmount{EUR, UINT64_C(1'989'987453007628), -12}));
5341 STAmount{EUR, UINT64_C(1'005'012546992372), -12},
5351 Env env(*
this, features);
5356 {alice, bob, carol, ed},
5358 {USD(2'000), EUR(2'000)});
5359 env(offer(carol, EUR(10), USD(10)));
5361 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
5362 env(pay(bob, ed, USD(10)),
5369 USD(1'005), EUR(1'000), ammAlice.
tokens()));
5378 Env env(*
this, features);
5383 {alice, bob, carol, ed},
5385 {USD(2'000), EUR(2'000)});
5386 env(offer(carol, EUR(9), USD(9)));
5388 AMM ammAlice(env, alice, USD(1'005), EUR(1'000),
false, 1'000);
5389 env(pay(bob, ed, USD(10)),
5395 env, bob,
STAmount{EUR, UINT64_C(1'989'993923296712), -12}));
5398 STAmount{EUR, UINT64_C(1'001'006076703288), -12},
5806 testcase(
"Offer/Strand Selection");
5807 using namespace jtx;
5810 auto const ETH = gw1[
"ETH"];
5811 auto const CAN = gw1[
"CAN"];
5817 auto prep = [&](
Env& env,
auto gwRate,
auto gw1Rate) {
5818 fund(env, gw, {alice, carol, bob, ed},
XRP(2'000), {USD(2'000)});
5823 {alice, carol, bob, ed},
5824 {ETH(2'000), CAN(2'000)},
5826 env(rate(gw, gwRate));
5827 env(rate(gw1, gw1Rate));
5831 for (
auto const& rates :
5846 for (
auto i = 0; i < 3; ++i)
5848 Env env(*
this, features);
5849 prep(env, rates.first, rates.second);
5851 if (i == 0 || i == 2)
5857 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5858 env(pay(carol, bob, USD(100)),
5865 BEAST_EXPECT(amm->expectBalances(
5866 USD(1'000), ETH(1'000), amm->tokens()));
5869 q[i] = Quality(Amounts{
5870 ETH(2'000) - env.
balance(carol, ETH),
5871 env.
balance(bob, USD) - USD(2'000)});
5874 BEAST_EXPECT(q[0] > q[1]);
5876 BEAST_EXPECT(q[0] == q[2]);
5883 for (
auto i = 0; i < 3; ++i)
5885 Env env(*
this, features);
5886 prep(env, rates.first, rates.second);
5888 if (i == 0 || i == 2)
5894 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5895 env(offer(alice, USD(400), ETH(400)));
5900 BEAST_EXPECT(amm->expectBalances(
5901 USD(1'000), ETH(1'000), amm->tokens()));
5903 if (i == 0 || i == 2)
5912 env, alice, 1, {Amounts{USD(400), ETH(400)}}));
5923 for (
auto i = 0; i < 3; ++i)
5925 Env env(*
this, features);
5926 prep(env, rates.first, rates.second);
5928 if (i == 0 || i == 2)
5934 amm.emplace(env, ed, USD(1'000), ETH(1'000));
5935 env(pay(carol, bob, USD(100)),
5942 BEAST_EXPECT(!amm->expectBalances(
5943 USD(1'000), ETH(1'000), amm->tokens()));
5945 if (i == 2 && !features[fixAMMv1_1])
5947 if (rates.first == 1.5)
5949 if (!features[fixAMMv1_1])
5957 UINT64_C(378'6327949540823),
5961 UINT64_C(283'9745962155617),
5971 UINT64_C(378'6327949540813),
5975 UINT64_C(283'974596215561),
5980 if (!features[fixAMMv1_1])
5988 UINT64_C(325'299461620749),
5992 UINT64_C(243'9745962155617),
6002 UINT64_C(325'299461620748),
6006 UINT64_C(243'974596215561),
6012 if (rates.first == 1.5)
6020 ETH, UINT64_C(378'6327949540812), -13},
6023 UINT64_C(283'9745962155609),
6034 ETH, UINT64_C(325'2994616207479), -13},
6037 UINT64_C(243'9745962155609),
6042 q[i] = Quality(Amounts{
6043 ETH(2'000) - env.
balance(carol, ETH),
6044 env.
balance(bob, USD) - USD(2'000)});
6047 BEAST_EXPECT(q[1] > q[0]);
6049 BEAST_EXPECT(q[2] > q[1]);
6053 for (
auto i = 0; i < 3; ++i)
6055 Env env(*
this, features);
6056 prep(env, rates.first, rates.second);
6058 if (i == 0 || i == 2)
6064 amm.emplace(env, ed, USD(1'000), ETH(1'000));
6065 env(offer(alice, USD(250), ETH(400)));
6070 BEAST_EXPECT(!amm->expectBalances(
6071 USD(1'000), ETH(1'000), amm->tokens()));
6077 if (rates.first == 1.5)
6079 if (!features[fixAMMv1_1])
6082 env, ed, 1, {{Amounts{ETH(400), USD(250)}}}));
6089 USD, UINT64_C(40'5694150420947), -13},
6091 ETH, UINT64_C(64'91106406735152), -14},
6105 ETH, UINT64_C(335'0889359326475), -13},
6107 USD, UINT64_C(209'4305849579047), -13},
6114 if (!features[fixAMMv1_1])
6123 ETH, UINT64_C(335'0889359326485), -13},
6125 USD, UINT64_C(209'4305849579053), -13},
6138 ETH, UINT64_C(335'0889359326475), -13},
6140 USD, UINT64_C(209'4305849579047), -13},
6166 for (
auto i = 0; i < 3; ++i)
6168 Env env(*
this, features);
6169 prep(env, rates.first, rates.second);
6172 if (i == 0 || i == 2)
6180 amm.emplace(env, ed, ETH(1'000), USD(1'000));
6182 env(pay(carol, bob, USD(100)),
6190 if (i == 2 && !features[fixAMMv1_1])
6192 if (rates.first == 1.5)
6195 BEAST_EXPECT(amm->expectBalances(
6196 STAmount{ETH, UINT64_C(1'176'66038955758), -11},
6202 BEAST_EXPECT(amm->expectBalances(
6204 ETH, UINT64_C(1'179'540094339627), -12},
6205 STAmount{USD, UINT64_C(847'7880529867501), -13},
6214 UINT64_C(343'3179205198749),
6218 UINT64_C(343'3179205198749),
6224 UINT64_C(362'2119470132499),
6228 UINT64_C(362'2119470132499),
6235 if (rates.first == 1.5)
6238 BEAST_EXPECT(amm->expectBalances(
6240 ETH, UINT64_C(1'176'660389557593), -12},
6246 BEAST_EXPECT(amm->expectBalances(
6247 STAmount{ETH, UINT64_C(1'179'54009433964), -11},
6248 STAmount{USD, UINT64_C(847'7880529867501), -13},
6257 UINT64_C(343'3179205198749),
6261 UINT64_C(343'3179205198749),
6267 UINT64_C(362'2119470132499),
6271 UINT64_C(362'2119470132499),
6276 q[i] = Quality(Amounts{
6277 ETH(2'000) - env.
balance(carol, ETH),
6278 env.
balance(bob, USD) - USD(2'000)});
6280 BEAST_EXPECT(q[1] > q[0]);
6281 BEAST_EXPECT(q[2] > q[0] && q[2] < q[1]);
6389 testcase(
"Fix changeSpotPriceQuality");
6390 using namespace jtx;
6395 SucceedShouldSucceedResize,
6407 auto const xrpIouAmounts10_100 =
6409 auto const iouXrpAmounts10_100 =
6414 {
"0.001519763260828713",
"1558701", Quality{5414253689393440221}, 1000, FailShouldSucceed},
6415 {
"0.01099814367603737",
"1892611", Quality{5482264816516900274}, 1000, FailShouldSucceed},
6416 {
"0.78",
"796599", Quality{5630392334958379008}, 1000, FailShouldSucceed},
6417 {
"105439.2955578965",
"49398693", Quality{5910869983721805038}, 400, FailShouldSucceed},
6418 {
"12408293.23445213",
"4340810521", Quality{5911611095910090752}, 997, FailShouldSucceed},
6419 {
"1892611",
"0.01099814367603737", Quality{6703103457950430139}, 1000, FailShouldSucceed},
6420 {
"423028.8508101858",
"3392804520", Quality{5837920340654162816}, 600, FailShouldSucceed},
6421 {
"44565388.41001027",
"73890647", Quality{6058976634606450001}, 1000, FailShouldSucceed},
6422 {
"66831.68494832662",
"16", Quality{6346111134641742975}, 0, FailShouldSucceed},
6423 {
"675.9287302203422",
"1242632304", Quality{5625960929244093294}, 300, FailShouldSucceed},
6424 {
"7047.112186735699",
"1649845866", Quality{5696855348026306945}, 504, FailShouldSucceed},
6425 {
"840236.4402981238",
"47419053", Quality{5982561601648018688}, 499, FailShouldSucceed},
6426 {
"992715.618909774",
"189445631733", Quality{5697835648288106944}, 815, SucceedShouldSucceedResize},
6427 {
"504636667521",
"185545883.9506651", Quality{6343802275337659280}, 503, SucceedShouldSucceedResize},
6428 {
"992706.7218636649",
"189447316000", Quality{5697835648288106944}, 797, SucceedShouldSucceedResize},
6429 {
"1.068737911388205",
"127860278877", Quality{5268604356368739396}, 293, SucceedShouldSucceedResize},
6430 {
"17932506.56880419",
"189308.6043676173", Quality{6206460598195440068}, 311, SucceedShouldSucceedResize},
6431 {
"1.066379294658174",
"128042251493", Quality{5268559341368739328}, 270, SucceedShouldSucceedResize},
6432 {
"350131413924",
"1576879.110907892", Quality{6487411636539049449}, 650,
Fail},
6433 {
"422093460",
"2.731797662057464", Quality{6702911108534394924}, 1000,
Fail},
6434 {
"76128132223",
"367172.7148422662", Quality{6487263463413514240}, 548,
Fail},
6435 {
"132701839250",
"280703770.7695443", Quality{6273750681188885075}, 562,
Fail},
6436 {
"994165.7604612011",
"189551302411", Quality{5697835592690668727}, 815,
Fail},
6437 {
"45053.33303227917",
"86612695359", Quality{5625695218943638190}, 500,
Fail},
6438 {
"199649.077043865",
"14017933007", Quality{5766034667318524880}, 324,
Fail},
6439 {
"27751824831.70903",
"78896950", Quality{6272538159621630432}, 500,
Fail},
6440 {
"225.3731275781907",
"156431793648", Quality{5477818047604078924}, 989,
Fail},
6441 {
"199649.077043865",
"14017933007", Quality{5766036094462806309}, 324,
Fail},
6442 {
"3.590272027140361",
"20677643641", Quality{5406056147042156356}, 808,
Fail},
6443 {
"1.070884664490231",
"127604712776", Quality{5268620608623825741}, 293,
Fail},
6444 {
"3272.448829820197",
"6275124076", Quality{5625710328924117902}, 81,
Fail},
6445 {
"0.009059512633902926",
"7994028", Quality{5477511954775533172}, 1000,
Fail},
6446 {
"1",
"1.0", Quality{0}, 100,
Fail},
6447 {
"1.0",
"1", Quality{0}, 100,
Fail},
6448 {
"10",
"10.0", Quality{xrpIouAmounts10_100}, 100,
Fail},
6449 {
"10.0",
"10", Quality{iouXrpAmounts10_100}, 100,
Fail},
6450 {
"69864389131",
"287631.4543025075", Quality{6487623473313516078}, 451, Succeed},
6451 {
"4328342973",
"12453825.99247381", Quality{6272522264364865181}, 997, Succeed},
6452 {
"32347017",
"7003.93031579449", Quality{6347261126087916670}, 1000, Succeed},
6453 {
"61697206161",
"36631.4583206413", Quality{6558965195382476659}, 500, Succeed},
6454 {
"1654524979",
"7028.659825511603", Quality{6487551345110052981}, 504, Succeed},
6455 {
"88621.22277293179",
"5128418948", Quality{5766347291552869205}, 380, Succeed},
6456 {
"1892611",
"0.01099814367603737", Quality{6703102780512015436}, 1000, Succeed},
6457 {
"4542.639373338766",
"24554809", Quality{5838994982188783710}, 0, Succeed},
6458 {
"5132932546",
"88542.99750172683", Quality{6419203342950054537}, 380, Succeed},
6459 {
"78929964.1549083",
"1506494795", Quality{5986890029845558688}, 589, Succeed},
6460 {
"10096561906",
"44727.72453735605", Quality{6487455290284644551}, 250, Succeed},
6461 {
"5092.219565514988",
"8768257694", Quality{5626349534958379008}, 503, Succeed},
6462 {
"1819778294",
"8305.084302902864", Quality{6487429398998540860}, 415, Succeed},
6463 {
"6970462.633911943",
"57359281", Quality{6054087899185946624}, 850, Succeed},
6464 {
"3983448845",
"2347.543644281467", Quality{6558965195382476659}, 856, Succeed},
6468 {
"771493171",
"1.243473020567508", Quality{6707566798038544272}, 100, SucceedShouldFail},
6472 boost::regex rx(
"^\\d+$");
6473 boost::smatch match;
6477 auto rules = env.
current()->rules();
6479 for (
auto const& t : tests)
6489 auto const poolInIsXRP =
6491 auto const poolOutIsXRP =
6493 assert(!(poolInIsXRP && poolOutIsXRP));
6494 auto const poolIn = getPool(
std::get<0>(t), poolInIsXRP);
6495 auto const poolOut = getPool(
std::get<1>(t), poolOutIsXRP);
6499 Amounts{poolIn, poolOut},
6506 if (status == SucceedShouldSucceedResize)
6508 if (!features[fixAMMv1_1])
6509 BEAST_EXPECT(Quality{*amounts} < quality);
6511 BEAST_EXPECT(Quality{*amounts} >= quality);
6513 else if (status == Succeed)
6515 if (!features[fixAMMv1_1])
6517 Quality{*amounts} >= quality ||
6519 Quality{*amounts}, quality,
Number{1, -7}));
6521 BEAST_EXPECT(Quality{*amounts} >= quality);
6523 else if (status == FailShouldSucceed)
6526 features[fixAMMv1_1] &&
6527 Quality{*amounts} >= quality);
6529 else if (status == SucceedShouldFail)
6532 !features[fixAMMv1_1] &&
6533 Quality{*amounts} < quality &&
6535 Quality{*amounts}, quality,
Number{1, -7}));
6544 if (status ==
Fail && quality != Quality{0})
6546 auto tinyOffer = [&]() {
6553 Amounts{poolIn, poolOut},
6557 else if (
isXRP(poolOut))
6562 Amounts{poolIn, poolOut},
6567 auto const takerPays = toAmount<STAmount>(
6572 Amounts{poolIn, poolOut}, takerPays, tfee)};
6574 BEAST_EXPECT(Quality(tinyOffer) < quality);
6576 else if (status == FailShouldSucceed)
6578 BEAST_EXPECT(!features[fixAMMv1_1]);
6580 else if (status == SucceedShouldFail)
6582 BEAST_EXPECT(features[fixAMMv1_1]);
6589 !strcmp(e.
what(),
"changeSpotPriceQuality failed"));
6591 !features[fixAMMv1_1] && status == FailShouldSucceed);
6600 BEAST_EXPECT(!res.has_value());
6666 using namespace jtx;
6672 Account const gatehub{
"gatehub"};
6673 Account const bitstamp{
"bitstamp"};
6674 Account const trader{
"trader"};
6675 auto const usdGH = gatehub[
"USD"];
6676 auto const btcGH = gatehub[
"BTC"];
6677 auto const usdBIT = bitstamp[
"USD"];
6681 char const* testCase;
6682 double const poolUsdBIT;
6683 double const poolUsdGH;
6696 double const offer1BtcGH = 0.1;
6697 double const offer2BtcGH = 0.1;
6698 double const offer2UsdGH = 1;
6699 double const rateBIT = 0.0;
6700 double const rateGH = 0.0;
6705 for (
auto const& input : {
6707 .testCase =
"Test Fix Overflow Offer",
6710 .sendMaxUsdBIT{usdBIT(50)},
6711 .sendUsdGH{usdGH,
uint64_t(272'455089820359), -12},
6714 .failUsdBIT{usdBIT,
uint64_t(46'47826086956522), -14},
6715 .failUsdBITr{usdBIT,
uint64_t(46'47826086956521), -14},
6716 .goodUsdGH{usdGH,
uint64_t(96'7543114220382), -13},
6717 .goodUsdGHr{usdGH,
uint64_t(96'7543114222965), -13},
6718 .goodUsdBIT{usdBIT,
uint64_t(8'464739069120721), -15},
6719 .goodUsdBITr{usdBIT,
uint64_t(8'464739069098152), -15},
6720 .lpTokenBalance = {28'61817604250837, -14},
6721 .lpTokenBalanceAlt =
IOUAmount{28'61817604250836, -14},
6729 .testCase =
"Overflow test {1, 100, 0.111}",
6732 .sendMaxUsdBIT{usdBIT(0.111)},
6733 .sendUsdGH{usdGH, 100},
6736 .failUsdBIT{usdBIT,
uint64_t(1'111), -3},
6737 .failUsdBITr{usdBIT,
uint64_t(1'111), -3},
6738 .goodUsdGH{usdGH,
uint64_t(90'04347888284115), -14},
6739 .goodUsdGHr{usdGH,
uint64_t(90'04347888284201), -14},
6740 .goodUsdBIT{usdBIT,
uint64_t(1'111), -3},
6741 .goodUsdBITr{usdBIT,
uint64_t(1'111), -3},
6742 .lpTokenBalance{10, 0},
6743 .offer1BtcGH = 1e-5,
6745 .offer2UsdGH = 1e-5,
6750 .testCase =
"Overflow test {1, 100, 1.00}",
6753 .sendMaxUsdBIT{usdBIT(1.00)},
6754 .sendUsdGH{usdGH, 100},
6757 .failUsdBIT{usdBIT,
uint64_t(2), 0},
6758 .failUsdBITr{usdBIT,
uint64_t(2), 0},
6759 .goodUsdGH{usdGH,
uint64_t(52'94379354424079), -14},
6760 .goodUsdGHr{usdGH,
uint64_t(52'94379354424135), -14},
6761 .goodUsdBIT{usdBIT,
uint64_t(2), 0},
6762 .goodUsdBITr{usdBIT,
uint64_t(2), 0},
6763 .lpTokenBalance{10, 0},
6764 .offer1BtcGH = 1e-5,
6766 .offer2UsdGH = 1e-5,
6771 .testCase =
"Overflow test {1, 100, 4.6432}",
6774 .sendMaxUsdBIT{usdBIT(4.6432)},
6775 .sendUsdGH{usdGH, 100},
6778 .failUsdBIT{usdBIT,
uint64_t(5'6432), -4},
6779 .failUsdBITr{usdBIT,
uint64_t(5'6432), -4},
6780 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6781 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6782 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
6783 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
6784 .lpTokenBalance{10, 0},
6785 .offer1BtcGH = 1e-5,
6787 .offer2UsdGH = 1e-5,
6792 .testCase =
"Overflow test {1, 100, 10}",
6795 .sendMaxUsdBIT{usdBIT(10)},
6796 .sendUsdGH{usdGH, 100},
6799 .failUsdBIT{usdBIT,
uint64_t(11), 0},
6800 .failUsdBITr{usdBIT,
uint64_t(11), 0},
6801 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6802 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6803 .goodUsdBIT{usdBIT,
uint64_t(2'821579689703915), -15},
6804 .goodUsdBITr{usdBIT,
uint64_t(2'821579689703954), -15},
6805 .lpTokenBalance{10, 0},
6806 .offer1BtcGH = 1e-5,
6808 .offer2UsdGH = 1e-5,
6813 .testCase =
"Overflow test {50, 100, 5.55}",
6816 .sendMaxUsdBIT{usdBIT(5.55)},
6817 .sendUsdGH{usdGH, 100},
6820 .failUsdBIT{usdBIT,
uint64_t(55'55), -2},
6821 .failUsdBITr{usdBIT,
uint64_t(55'55), -2},
6822 .goodUsdGH{usdGH,
uint64_t(90'04347888284113), -14},
6823 .goodUsdGHr{usdGH,
uint64_t(90'0434788828413), -13},
6824 .goodUsdBIT{usdBIT,
uint64_t(55'55), -2},
6825 .goodUsdBITr{usdBIT,
uint64_t(55'55), -2},
6826 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
6827 .offer1BtcGH = 1e-5,
6829 .offer2UsdGH = 1e-5,
6834 .testCase =
"Overflow test {50, 100, 50.00}",
6837 .sendMaxUsdBIT{usdBIT(50.00)},
6838 .sendUsdGH{usdGH, 100},
6839 .failUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
6840 .failUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
6841 .failUsdBIT{usdBIT,
uint64_t(100), 0},
6842 .failUsdBITr{usdBIT,
uint64_t(100), 0},
6843 .goodUsdGH{usdGH,
uint64_t(52'94379354424081), -14},
6844 .goodUsdGHr{usdGH,
uint64_t(52'94379354424092), -14},
6845 .goodUsdBIT{usdBIT,
uint64_t(100), 0},
6846 .goodUsdBITr{usdBIT,
uint64_t(100), 0},
6847 .lpTokenBalance{
uint64_t(70'71067811865475), -14},
6848 .offer1BtcGH = 1e-5,
6850 .offer2UsdGH = 1e-5,
6855 .testCase =
"Overflow test {50, 100, 232.16}",
6858 .sendMaxUsdBIT{usdBIT(232.16)},
6859 .sendUsdGH{usdGH, 100},
6862 .failUsdBIT{usdBIT,
uint64_t(282'16), -2},
6863 .failUsdBITr{usdBIT,
uint64_t(282'16), -2},
6864 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6865 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6866 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
6867 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
6868 .lpTokenBalance{70'71067811865475, -14},
6869 .offer1BtcGH = 1e-5,
6871 .offer2UsdGH = 1e-5,
6876 .testCase =
"Overflow test {50, 100, 500}",
6879 .sendMaxUsdBIT{usdBIT(500)},
6880 .sendUsdGH{usdGH, 100},
6883 .failUsdBIT{usdBIT,
uint64_t(550), 0},
6884 .failUsdBITr{usdBIT,
uint64_t(550), 0},
6885 .goodUsdGH{usdGH,
uint64_t(35'44113971506987), -14},
6886 .goodUsdGHr{usdGH,
uint64_t(35'44113971506987), -14},
6887 .goodUsdBIT{usdBIT,
uint64_t(141'0789844851958), -13},
6888 .goodUsdBITr{usdBIT,
uint64_t(141'0789844851962), -13},
6889 .lpTokenBalance{70'71067811865475, -14},
6890 .offer1BtcGH = 1e-5,
6892 .offer2UsdGH = 1e-5,
6898 testcase(input.testCase);
6899 for (
auto const& features :
6900 {all - fixAMMOverflowOffer - fixAMMv1_1 - fixAMMv1_3, all})
6904 env.
fund(
XRP(5'000), gatehub, bitstamp, trader);
6907 if (input.rateGH != 0.0)
6908 env(rate(gatehub, input.rateGH));
6909 if (input.rateBIT != 0.0)
6910 env(rate(bitstamp, input.rateBIT));
6912 env(trust(trader, usdGH(10'000'000)));
6913 env(trust(trader, usdBIT(10'000'000)));
6914 env(trust(trader, btcGH(10'000'000)));
6917 env(pay(gatehub, trader, usdGH(100'000)));
6918 env(pay(gatehub, trader, btcGH(100'000)));
6919 env(pay(bitstamp, trader, usdBIT(100'000)));
6925 usdGH(input.poolUsdGH),
6926 usdBIT(input.poolUsdBIT)};
6930 amm.getLPTokensBalance();
6932 env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
6935 btcGH(input.offer2BtcGH),
6936 usdGH(input.offer2UsdGH)));
6939 env(pay(trader, trader, input.sendUsdGH),
6941 path(~btcGH, ~usdGH),
6946 auto const failUsdGH =
6947 features[fixAMMv1_1] ? input.failUsdGHr : input.failUsdGH;
6948 auto const failUsdBIT =
6949 features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT;
6950 auto const goodUsdGH =
6951 features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH;
6952 auto const goodUsdBIT =
6953 features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT;
6954 auto const lpTokenBalance =
6955 env.
enabled(fixAMMv1_3) && input.lpTokenBalanceAlt
6956 ? *input.lpTokenBalanceAlt
6957 : input.lpTokenBalance;
6958 if (!features[fixAMMOverflowOffer])
6960 BEAST_EXPECT(amm.expectBalances(
6961 failUsdGH, failUsdBIT, lpTokenBalance));
6965 BEAST_EXPECT(amm.expectBalances(
6966 goodUsdGH, goodUsdBIT, lpTokenBalance));
6971 amm.getLPTokensBalance() == preSwapLPTokenBalance);
6975 Number const sqrtPoolProduct =
6976 root2(goodUsdGH * goodUsdBIT);
6987 (sqrtPoolProduct +
Number{1, -14} >=
6988 input.lpTokenBalance));