454 testcase(
"Rm small increased q offers MPT");
462 using namespace std::chrono_literals;
463 auto const alice =
Account{
"alice"};
464 auto const bob =
Account{
"bob"};
465 auto const carol =
Account{
"carol"};
468 auto test = [&](
auto&& issue1,
auto&& issue2) {
489 for (
auto crossBothOffers : {
false,
true})
491 Env env{*
this, features};
493 env.
fund(
XRP(10'000), alice, bob, carol, gw);
496 auto const usd = issue1(
500 .holders = {alice, bob, carol},
501 .
limit = 100'000'000});
502 auto const eur = issue2(
506 .holders = {alice, bob, carol},
507 .
limit = 100'000'000});
509 auto initialCarolUSD = tinyAmount(usd);
510 env(
pay(gw, carol, initialCarolUSD));
511 env(
pay(gw, bob, usd(100'000)));
512 env(
pay(gw, alice, eur(100'000)));
515 env(
offer(carol, eur(10), usd(10'000)));
518 env(
offer(bob, eur(10), usd(5'000), tfPassive));
525 auto aliceTakerGets = crossBothOffers ? eur(2) : eur(1);
526 env(
offer(alice, usd(1'000), aliceTakerGets));
534 auto const balanceCarolUSD = kIsEuriou ? usd(0) : initialCarolUSD;
545 auto const balanceAliceUSD = kIsEuriou ? usd(1) : usd(0);
551 for (
auto partialPayment : {
false,
true})
553 Env env{*
this, features};
555 env.
fund(
XRP(10'000), alice, bob, carol, gw);
558 auto const usd = issue1(
562 .holders = {alice, bob, carol},
563 .
limit = 100'000'000});
564 auto const eur = issue2(
568 .holders = {alice, bob, carol},
569 .
limit = 100'000'000});
571 auto const initialCarolUSD = tinyAmount(usd);
572 env(
pay(gw, carol, initialCarolUSD));
573 env(
pay(gw, bob, usd(100'000)));
574 env(
pay(gw, alice, eur(100'000)));
577 env(
offer(carol, eur(10), usd(2'000)));
579 env(
offer(bob, eur(20), usd(4'000), tfPassive));
584 partialPayment ? (tfNoRippleDirect | tfPartialPayment) : tfNoRippleDirect;
588 env(
pay(alice, bob, usd(5'000)),
602 auto const balanceCarolUSD = kIsEuriou ? usd(0) : initialCarolUSD;
676 auto const alice =
Account{
"alice"};
677 auto const bob =
Account{
"bob"};
680 testcase(
"Partially funded MPT/XRP input offer cannot be consumed for free");
682 Env env{*
this, features};
685 env.
fund(
XRP(10'000), gw, alice, bob);
688 MPTTester const usd({.env = env, .issuer = gw, .holders = {alice}});
690 auto const aliceOfferSeq = env.
seq(alice);
694 auto const targetBalance =
reserve(env, 2) +
drops(999'999);
700 auto const aliceXRPBefore = env.
balance(alice);
701 auto const bobXRPBefore = env.
balance(bob);
706 Txflags(tfNoRippleDirect | tfPartialPayment),
721 BEAST_EXPECT(env.
balance(alice) == aliceXRPBefore);
722 BEAST_EXPECT(env.
balance(bob) == bobXRPBefore);
726 testcase(
"Partially funded MPT/IOU input offer cannot be consumed for free");
728 Env env{*
this, features};
729 auto const mptIssuer =
Account{
"mptIssuer"};
730 auto const iouIssuer =
Account{
"iouIssuer"};
732 env.
fund(
XRP(10'000), mptIssuer, iouIssuer, alice, bob);
735 auto const eur = iouIssuer[
"EUR"];
736 env.
trust(eur(100), alice, bob);
737 env(
pay(iouIssuer, alice, eur(0.5)));
740 MPTTester const usd({.env = env, .issuer = mptIssuer, .holders = {alice}});
742 auto const aliceOfferSeq = env.
seq(alice);
743 env(
offer(alice, usd(1), eur(1)));
746 auto const aliceEURBefore = env.
balance(alice, eur);
747 auto const bobEURBefore = env.
balance(bob, eur);
749 env(
pay(mptIssuer, bob, eur(1)),
752 Txflags(tfNoRippleDirect | tfPartialPayment),
762 BEAST_EXPECT(env.
balance(alice, eur) == aliceEURBefore);
763 BEAST_EXPECT(env.
balance(bob, eur) == bobEURBefore);
767 testcase(
"Partially funded MPT/MPT input offer cannot be consumed for free");
769 Env env{*
this, features};
770 auto const issuerA =
Account{
"issuerA"};
771 auto const issuerB =
Account{
"issuerB"};
773 env.
fund(
XRP(10'000), issuerA, issuerB, alice, bob);
776 MPTTester const usd({.env = env, .issuer = issuerA, .holders = {alice}});
777 MPTTester const eur({.env = env, .issuer = issuerB, .holders = {alice, bob}});
779 env(
pay(issuerB, alice, eur(999'999)));
782 auto const aliceOfferSeq = env.
seq(alice);
783 env(
offer(alice, usd(1), eur(1'000'000)));
786 auto const aliceEURBefore = eur.getBalance(alice);
787 auto const bobEURBefore = eur.getBalance(bob);
789 env(
pay(issuerA, bob, eur(1'000'000)),
792 Txflags(tfNoRippleDirect | tfPartialPayment),
802 BEAST_EXPECT(env.
balance(alice, eur) == eur(aliceEURBefore));
803 BEAST_EXPECT(env.
balance(bob, eur) == eur(bobEURBefore));
813 "Partially funded MPT input offer is removed, not consumed, "
814 "when a funded offer crosses");
816 Env env{*
this, features};
818 auto const carol =
Account{
"carol"};
820 env.
fund(
XRP(10'000), gw, alice, carol, bob);
823 MPTTester const usd({.env = env, .issuer = gw, .holders = {alice, carol, bob}});
829 auto const aliceOfferSeq = env.
seq(alice);
833 auto const targetBalance =
reserve(env, 2) +
drops(999'999);
841 auto const carolOfferSeq = env.
seq(carol);
846 env(
pay(gw, bob, usd(2)));
851 BEAST_EXPECT(env.
le(aliceOffer) !=
nullptr);
852 BEAST_EXPECT(env.
le(carolOffer) !=
nullptr);
854 auto const aliceXRPBefore = env.
balance(alice);
855 auto const bobXRPBefore = env.
balance(bob);
861 env(
offer(bob,
drops(2'000'000), usd(2), tfImmediateOrCancel));
864 BEAST_EXPECT(env.
le(aliceOffer) ==
nullptr);
865 BEAST_EXPECT(env.
le(carolOffer) ==
nullptr);
870 BEAST_EXPECT(env.
balance(alice) == aliceXRPBefore);
871 BEAST_EXPECT(env.
balance(alice, usd) == usd(0));
872 BEAST_EXPECT(env.
balance(carol, usd) == usd(1));
873 BEAST_EXPECT(env.
balance(bob, usd) == usd(1));
1220 testcase(
"MPT AMM limitQuality checks rounded integral output");
1222 using namespace jtx;
1233 Env env{*
this, features};
1235 env.
fund(
XRP(10'000), gw, alice, bob);
1241 .holders = {alice, bob},
1244 AMM const amm(env, alice,
XRP(100), btc(1'000));
1246 auto const bobBTCBefore = btc.getBalance(bob);
1247 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1252 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1253 BEAST_EXPECT(btc.getBalance(bob) == bobBTCBefore + 32);
1254 BEAST_EXPECT(xrpAfter > xrpBefore);
1255 BEAST_EXPECT(btcAfter < btcBefore);
1256 BEAST_EXPECT(lpAfter == lpBefore);
1265 Env env{*
this, features};
1267 env.
fund(
XRP(10'000), gw, alice, bob);
1273 .holders = {alice, bob},
1276 AMM const amm(env, alice,
XRP(100), btc(1'000));
1278 auto const bobBTCBefore = btc.getBalance(bob);
1279 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1281 env(
offer(bob, btc(100),
drops(10'340'000)));
1284 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1285 BEAST_EXPECT(btc.getBalance(bob) == bobBTCBefore + 32);
1286 BEAST_EXPECT(xrpAfter > xrpBefore);
1287 BEAST_EXPECT(btcAfter < btcBefore);
1288 BEAST_EXPECT(lpAfter == lpBefore);
1292 if (BEAST_EXPECT(bobOffers.size() == 1))
1294 BEAST_EXPECT((*bobOffers[0])[sfTakerPays] != btc(100));
1295 BEAST_EXPECT((*bobOffers[0])[sfTakerGets] !=
drops(10'340'000));
1304 Env env{*
this, features};
1306 env.
fund(
XRP(10'000), gw, alice, bob);
1312 .holders = {alice, bob},
1315 AMM const amm(env, alice,
drops(1'000), btc(100'000'000));
1317 auto const bobXRPBefore = env.
balance(bob,
XRP);
1318 auto const baseFee = env.
current()->fees().base;
1319 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1324 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1326 BEAST_EXPECT(xrpAfter < xrpBefore);
1327 BEAST_EXPECT(btcAfter > btcBefore);
1328 BEAST_EXPECT(lpAfter == lpBefore);
1336 Env env{*
this, features};
1338 env.
fund(
XRP(10'000), gw, alice, bob);
1344 .holders = {alice, bob},
1347 AMM const amm(env, alice,
drops(1'000), btc(100'000'000));
1349 auto const bobXRPBefore = env.
balance(bob,
XRP);
1350 auto const baseFee = env.
current()->fees().base;
1351 auto const [xrpBefore, btcBefore, lpBefore] =
amm.balances();
1353 env(
offer(bob,
drops(100), btc(10'340'000)));
1356 auto const [xrpAfter, btcAfter, lpAfter] =
amm.balances();
1358 BEAST_EXPECT(xrpAfter < xrpBefore);
1359 BEAST_EXPECT(btcAfter > btcBefore);
1360 BEAST_EXPECT(lpAfter == lpBefore);
1364 if (BEAST_EXPECT(bobOffers.size() == 1))
1366 BEAST_EXPECT((*bobOffers[0])[sfTakerPays] !=
drops(100));
1367 BEAST_EXPECT((*bobOffers[0])[sfTakerGets] != btc(10'340'000));
2431 using namespace jtx;
2433 auto const gw =
Account(
"gateway");
2435 Env env{*
this, features};
2437 env.
fund(
XRP(10'000'000), gw);
2440 auto musd =
MPTTester({.env = env, .issuer = gw});
2441 MPT const usd = musd;
2444 auto const f = env.
current()->fees().base;
2447 enum class PreAuthType { NoPreAuth, AcctPreAuth };
2453 PreAuthType preAuth;
2464 TestData
const tests[]{
2466 {.account=
"ann", .fundXrp=
reserve(env, 0) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=f, .balanceUsd=usd( 0), .offers=0, .owners=0},
2467 {.account=
"bev", .fundXrp=
reserve(env, 0) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=f, .balanceUsd=usd( 0), .offers=0, .owners=0},
2468 {.account=
"cam", .fundXrp=
reserve(env, 0) + 2 * f, .bookAmount=0, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tecINSUF_RESERVE_OFFER, .spentXrp=f, .balanceUsd=usd( 0), .offers=0, .owners=0},
2469 {.account=
"deb", .fundXrp=
reserve(env, 0) + 2 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=2 * f, .balanceUsd=usd( 1), .offers=0, .owners=1, .scale=100000},
2470 {.account=
"eve", .fundXrp=
reserve(env, 1) + 0 * f, .bookAmount=0, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=f, .balanceUsd=usd( 0), .offers=1, .owners=1},
2471 {.account=
"flo", .fundXrp=
reserve(env, 1) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + f, .balanceUsd=usd( 1), .offers=0, .owners=1},
2472 {.account=
"gay", .fundXrp=
reserve(env, 1) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 50) + f, .balanceUsd=usd( 50), .offers=0, .owners=1},
2473 {.account=
"hye", .fundXrp=
XRP(1000) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 800) + f, .balanceUsd=usd( 800), .offers=0, .owners=1},
2474 {.account=
"ivy", .fundXrp=
XRP( 1) +
reserve(env, 1) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + f, .balanceUsd=usd( 1), .offers=0, .owners=1},
2475 {.account=
"joy", .fundXrp=
XRP( 1) +
reserve(env, 2) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + f, .balanceUsd=usd( 1), .offers=1, .owners=2},
2476 {.account=
"kim", .fundXrp=
XRP( 900) +
reserve(env, 2) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=0, .owners=1},
2477 {.account=
"liz", .fundXrp=
XRP( 998) +
reserve(env, 0) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 998) + f, .balanceUsd=usd( 998), .offers=0, .owners=1},
2478 {.account=
"meg", .fundXrp=
XRP( 998) +
reserve(env, 1) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=0, .owners=1},
2479 {.account=
"nia", .fundXrp=
XRP( 998) +
reserve(env, 2) + 1 * f, .bookAmount=999, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=1, .owners=2},
2480 {.account=
"ova", .fundXrp=
XRP( 999) +
reserve(env, 0) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 999) + f, .balanceUsd=usd( 999), .offers=0, .owners=1},
2481 {.account=
"pam", .fundXrp=
XRP( 999) +
reserve(env, 1) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP(1000) + f, .balanceUsd=usd( 1000), .offers=0, .owners=1},
2482 {.account=
"rae", .fundXrp=
XRP( 999) +
reserve(env, 2) + 1 * f, .bookAmount=1000, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP(1000) + f, .balanceUsd=usd( 1000), .offers=0, .owners=1},
2483 {.account=
"sue", .fundXrp=
XRP(1000) +
reserve(env, 2) + 1 * f, .bookAmount=0, .preAuth=PreAuthType::NoPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=f, .balanceUsd=usd( 0), .offers=1, .owners=1},
2486 {.account=
"ned", .fundXrp=
reserve(env, 1) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2487 {.account=
"ole", .fundXrp=
reserve(env, 1) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2488 {.account=
"pat", .fundXrp=
reserve(env, 1) + 2 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2489 {.account=
"quy", .fundXrp=
reserve(env, 1) + 2 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecUNFUNDED_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2490 {.account=
"ron", .fundXrp=
reserve(env, 1) + 3 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecINSUF_RESERVE_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2491 {.account=
"syd", .fundXrp=
reserve(env, 1) + 3 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=3 * f, .balanceUsd=usd( 1), .offers=0, .owners=1, .scale=100000},
2492 {.account=
"ted", .fundXrp=
XRP( 20) +
reserve(env, 1) + 2 * f, .bookAmount=1000, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP(20) + 2 * f, .balanceUsd=usd( 20), .offers=0, .owners=1},
2493 {.account=
"uli", .fundXrp=
reserve(env, 2) + 0 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tecINSUF_RESERVE_OFFER, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=0, .owners=1},
2494 {.account=
"vic", .fundXrp=
reserve(env, 2) + 0 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + 2 * f, .balanceUsd=usd( 1), .offers=0, .owners=1},
2495 {.account=
"wes", .fundXrp=
reserve(env, 2) + 1 * f, .bookAmount=0, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=2 * f, .balanceUsd=usd( 0), .offers=1, .owners=2},
2496 {.account=
"xan", .fundXrp=
reserve(env, 2) + 1 * f, .bookAmount=1, .preAuth=PreAuthType::AcctPreAuth, .offerAmount=1000, .tec=
tesSUCCESS, .spentXrp=
XRP( 1) + 2 * f, .balanceUsd=usd( 1), .offers=1, .owners=2},
2500 for (
auto const& t :
tests)
2502 auto const acct =
Account(t.account);
2503 env.
fund(t.fundXrp, acct);
2510 auto const book = t.bookAmount;
2512 env(
offer(gw,
XRP(book), usd(book * t.scale)));
2519 if (t.preAuth == PreAuthType::AcctPreAuth)
2520 musd.authorize({.account = acct});
2525 auto const acctOffer = t.offerAmount;
2526 env(
offer(acct, usd(acctOffer * t.scale),
XRP(acctOffer)),
Ter(t.tec));
2531 auto const expBalanceUsd = [&]() {
2533 return t.balanceUsd;
2536 return usd(f.value() / 10);
2538 BEAST_EXPECT(env.
balance(acct, usd) == expBalanceUsd);
2539 BEAST_EXPECT(env.
balance(acct,
xrpIssue()) == t.fundXrp - t.spentXrp);
2544 BEAST_EXPECT(acctOffers.size() == t.offers);
2545 if (!acctOffers.empty() && t.offers != 0)
2547 auto const& acctOffer = *(acctOffers.front());
2549 auto const leftover = t.offerAmount - t.bookAmount;
2550 BEAST_EXPECT(acctOffer[sfTakerGets] ==
XRP(leftover));
2551 BEAST_EXPECT(acctOffer[sfTakerPays] == usd(leftover));
2554 if (t.preAuth == PreAuthType::NoPreAuth)
2556 if (t.balanceUsd.value().signum() != 0)
2559 BEAST_EXPECT(env.
balance(acct, usd) == expBalanceUsd);
2886 using namespace jtx;
2888 auto const gw =
Account(
"gateway");
2890 Env env{*
this, features};
2892 env.
fund(
XRP(10'000'000), gw);
2894 auto musd =
MPTTester({.env = env, .issuer = gw});
2895 MPT const usd = musd;
2898 auto const f = env.
current()->fees().base;
2934 : account(std::move(account))
2935 , fundXrp(std::move(fundXrp))
2936 , fundUSD(std::move(fundUsd))
2937 , gwGets(std::move(gwGets))
2938 , gwPays(std::move(gwPays))
2939 , acctGets(std::move(acctGets))
2940 , acctPays(std::move(acctPays))
2942 , spentXrp(std::move(spentXrp))
2943 , finalUsd(std::move(finalUsd))
2946 , takerGets(std::move(takerGets))
2947 , takerPays(std::move(takerPays))
2985 TestData
const tests[]{
2988 {
"ann",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd( 5), usd(10),
XRP(10),
tecINSUF_RESERVE_OFFER,
XRP( 0) + (1 * f), usd( 0), 0, 0},
2989 {
"bev",
XRP(10) +
reserve(env, 1) + 1 * f, usd( 0),
XRP(10), usd( 5), usd(10),
XRP(10),
tesSUCCESS,
XRP( 0) + (1 * f), usd( 0), 1, 1,
XRP(10), usd(10)},
2990 {
"cam",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(10), usd(10),
XRP(10),
tesSUCCESS,
XRP( 10) + (1 * f), usd(10), 0, 1},
2991 {
"deb",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(20), usd(10),
XRP(10),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 0, 1},
2992 {
"eve",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(20), usd( 5),
XRP( 5),
tesSUCCESS,
XRP( 5) + (1 * f), usd(10), 0, 1},
2993 {
"flo",
XRP(10) +
reserve(env, 0) + 1 * f, usd( 0),
XRP(10), usd(20), usd(20),
XRP(20),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 0, 1},
2994 {
"gay",
XRP(20) +
reserve(env, 1) + 1 * f, usd( 0),
XRP(10), usd(20), usd(20),
XRP(20),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 0, 1},
2995 {
"hye",
XRP(20) +
reserve(env, 2) + 1 * f, usd( 0),
XRP(10), usd(20), usd(20),
XRP(20),
tesSUCCESS,
XRP( 10) + (1 * f), usd(20), 1, 2,
XRP(10), usd(10)},
2997 {
"meg",
reserve(env, 1) + 2 * f, usd(10), usd(10),
XRP( 5),
XRP(10), usd(10),
tecINSUF_RESERVE_OFFER,
XRP( 0) + (2 * f), usd(10), 0, 1},
2998 {
"nia",
reserve(env, 2) + 2 * f, usd(10), usd(10),
XRP( 5),
XRP(10), usd(10),
tesSUCCESS,
XRP( 0) + (2 * f), usd(10), 1, 2, usd(10),
XRP(10)},
2999 {
"ova",
reserve(env, 1) + 2 * f, usd(10), usd(10),
XRP(10),
XRP(10), usd(10),
tesSUCCESS,
XRP(-10) + (2 * f), usd( 0), 0, 1},
3000 {
"pam",
reserve(env, 1) + 2 * f, usd(10), usd(10),
XRP(20),
XRP(10), usd(10),
tesSUCCESS,
XRP(-20) + (2 * f), usd( 0), 0, 1},
3001 {
"qui",
reserve(env, 1) + 2 * f, usd(10), usd(20),
XRP(40),
XRP(10), usd(10),
tesSUCCESS,
XRP(-20) + (2 * f), usd( 0), 0, 1},
3002 {
"rae",
reserve(env, 2) + 2 * f, usd(10), usd( 5),
XRP( 5),
XRP(10), usd(10),
tesSUCCESS,
XRP( -5) + (2 * f), usd( 5), 1, 2, usd( 5),
XRP( 5)},
3003 {
"sue",
reserve(env, 2) + 2 * f, usd(10), usd( 5),
XRP(10),
XRP(10), usd(10),
tesSUCCESS,
XRP(-10) + (2 * f), usd( 5), 1, 2, usd( 5),
XRP( 5)},
3007 auto const zeroUsd = usd(0);
3008 for (
auto const& t :
tests)
3013 auto const acct =
Account(t.account);
3015 env.
fund(t.fundXrp, acct);
3021 if (t.fundUSD != zeroUsd)
3023 musd.authorize({.account = acct});
3025 env(
pay(gw, acct, t.fundUSD));
3029 env(
offer(gw, t.gwGets, t.gwPays));
3034 env(
offer(acct, t.acctGets, t.acctPays, tfSell),
Ter(t.tec));
3039 BEAST_EXPECT(env.
balance(acct, usd) == t.finalUsd);
3040 BEAST_EXPECT(env.
balance(acct,
xrpIssue()) == t.fundXrp - t.spentXrp);
3047 if (!acctOffers.empty())
3049 BEAST_EXPECT(acctOffers.size() == 1);
3050 auto const& acctOffer = *(acctOffers.front());
3052 BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER);
3053 BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets);
3054 BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays);
3149 using namespace jtx;
3150 auto const gw1 =
Account(
"gateway1");
3153 auto const issuer =
Account(
"issuer");
3154 auto const sender =
Account(
"sender");
3155 auto const receiver =
Account(
"receiver");
3156 auto const seller =
Account(
"seller");
3157 auto const buyer =
Account(
"buyer");
3159 Env env{*
this, features};
3160 env.
fund(
XRP(10'000), issuer, sender, receiver, seller, buyer);
3166 .holders = {sender, receiver, seller, buyer},
3167 .transferFee = 100}};
3170 mpt.
pay(issuer, sender, 2'000);
3171 mpt.
pay(issuer, seller, 2'000);
3180 BEAST_EXPECT(mpt.
getBalance(sender) == 1'000);
3181 BEAST_EXPECT(mpt.
getBalance(receiver) == 999);
3182 BEAST_EXPECT(mpt.
getBalance(issuer) == 3'999);
3191 BEAST_EXPECT(mpt.
getBalance(seller) == 1'000);
3193 BEAST_EXPECT(mpt.
getBalance(issuer) == 3'998);
3196 auto test = [&](
auto&& issue1,
auto&& issue2) {
3197 Env env{*
this, features};
3200 auto const fee = env.
current()->fees().base;
3206 issue1({.env = env, .token =
"USD", .issuer = gw1, .transferFee = 25'000});
3209 auto const ann =
Account(
"ann");
3210 auto const bob =
Account(
"bob");
3217 musd.authorize({.account = ann});
3218 musd.authorize({.account = bob});
3222 env(
trust(ann, usd(20'000)));
3223 env(
trust(bob, usd(20'000)));
3227 env(
pay(gw1, bob, usd(12'500)));
3236 env(
offer(bob,
XRP(1), usd(10'000)));
3239 env(
offer(ann, usd(10'000),
XRP(1)));
3254 auto const che =
Account(
"che");
3255 auto const deb =
Account(
"deb");
3262 musd.authorize({.account = che});
3263 musd.authorize({.account = deb});
3267 env(
trust(che, usd(20'000)));
3268 env(
trust(deb, usd(20'000)));
3272 env(
pay(gw1, deb, usd(12'500)));
3275 env(
offer(che, usd(10'000),
XRP(1)));
3278 env(
offer(deb,
XRP(1), usd(10'000)));
3290 auto const eve =
Account(
"eve");
3291 auto const fyn =
Account(
"fyn");
3293 env.
fund(
XRP(20'000) + (fee * 2), eve, fyn);
3299 musd.authorize({.account = eve});
3300 musd.authorize({.account = fyn});
3304 env(
trust(eve, usd(20'000)));
3305 env(
trust(fyn, usd(20'000)));
3309 env(
pay(gw1, eve, usd(10'000)));
3310 env(
pay(gw1, fyn, usd(10'000)));
3316 env(
offer(eve, usd(1'000),
XRP(4'000)));
3320 env(
offer(fyn,
XRP(2'000), usd(500)));
3326 BEAST_EXPECT(evesOffers.size() == 1);
3327 if (!evesOffers.empty())
3329 auto const& evesOffer = *(evesOffers.front());
3330 BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER);
3331 BEAST_EXPECT(evesOffer[sfTakerGets] ==
XRP(2'000));
3332 BEAST_EXPECT(evesOffer[sfTakerPays] == usd(500));
3341 auto const gw2 =
Account(
"gateway2");
3347 issue2({.env = env, .token =
"EUR", .issuer = gw2, .transferFee = 50'000});
3353 auto const gay =
Account(
"gay");
3354 auto const hal =
Account(
"hal");
3361 musd.authorize({.account = gay});
3362 musd.authorize({.account = hal});
3366 env(
trust(gay, usd(20'000)));
3367 env(
trust(hal, usd(20'000)));
3373 meur.authorize({.account = gay});
3374 meur.authorize({.account = hal});
3378 env(
trust(gay, eur(20'000)));
3379 env(
trust(hal, eur(20'000)));
3383 env(
pay(gw1, gay, usd(12'500)));
3384 env(
pay(gw2, hal, eur(150)));
3387 env(
offer(gay, eur(100), usd(10'000)));
3390 env(
offer(hal, usd(10'000), eur(100)));
3406 auto const ova =
Account(
"ova");
3407 auto const pat =
Account(
"pat");
3408 auto const qae =
Account(
"qae");
3418 musd.authorize({.account = ova});
3419 musd.authorize({.account = pat});
3420 musd.authorize({.account = qae});
3424 env(
trust(ova, usd(20'000)));
3425 env(
trust(pat, usd(20'000)));
3426 env(
trust(qae, usd(20'000)));
3432 meur.authorize({.account = ova});
3433 meur.authorize({.account = pat});
3434 meur.authorize({.account = qae});
3438 env(
trust(ova, eur(20'000)));
3439 env(
trust(pat, eur(20'000)));
3440 env(
trust(qae, eur(20'000)));
3444 env(
pay(gw1, ova, usd(12'500)));
3445 env(
pay(gw2, qae, eur(150)));
3448 env(
offer(ova,
XRP(2), usd(10'000)));
3452 env(
offer(qae, usd(10'000), eur(100)));
3461 if (!ovasOffers.empty())
3463 BEAST_EXPECT(ovasOffers.size() == 1);
3464 auto const& ovasOffer = *(ovasOffers.front());
3466 BEAST_EXPECT(ovasOffer[sfLedgerEntryType] == ltOFFER);
3467 BEAST_EXPECT(ovasOffer[sfTakerGets] == usd(0));
3468 BEAST_EXPECT(ovasOffer[sfTakerPays] ==
XRP(0));
3489 auto const gw =
Account(
"gw_tr1");
3490 auto const alice =
Account(
"alice_tr1");
3491 auto const bob =
Account(
"bob_tr1");
3493 Env env{*
this, features};
3494 env.
fund(
XRP(10'000), gw, alice, bob);
3498 {.env = env, .issuer = gw, .holders = {alice, bob}, .transferFee = 25'000});
3501 env(
pay(gw, alice, usd(1'250)));
3503 env(
offer(bob, usd(1'000),
XRP(1'000)));
3514 BEAST_EXPECT(env.
balance(alice, usd) == usd(0));
3516 BEAST_EXPECT(env.
balance(bob, usd) == usd(1'000));
3526 auto const gw =
Account(
"gw_tr2");
3527 auto const gw2 =
Account(
"gw2_tr2");
3528 auto const alice =
Account(
"alice_tr2");
3529 auto const bob =
Account(
"bob_tr2");
3530 auto const carol =
Account(
"carol_tr2");
3532 Env env{*
this, features};
3533 env.
fund(
XRP(10'000), gw, gw2, alice, bob, carol);
3537 {.env = env, .issuer = gw, .holders = {bob, carol}, .transferFee = 25'000});
3538 auto const gusd = gw2[
"USD"];
3540 env(
trust(alice, gusd(10'000)));
3541 env(
trust(bob, gusd(10'000)));
3544 env(
pay(gw2, alice, gusd(1'000)));
3545 env(
pay(gw, bob, musd(1'000)));
3549 env(
offer(bob, gusd(1'000), musd(1'000)));
3551 env(
offer(carol, musd(800),
XRP(800)));
3563 BEAST_EXPECT(env.
balance(alice, gusd) == gusd(0));
3565 BEAST_EXPECT(env.
balance(bob, musd) == musd(0));
3567 BEAST_EXPECT(env.
balance(carol, musd) == musd(800));
3574 testcase(
"Transfer Rate Overflow Offer");
3576 using namespace jtx;
3578 auto const issuer =
Account(
"issuer");
3579 auto const taker =
Account(
"taker");
3582 Env env{*
this, features};
3583 env.
fund(
XRP(10'000), issuer, taker);
3586 auto constexpr takerFunds = 2'000'000'000'000'000'000LL;
3591 .transferFee = 50'000,
3598 auto constexpr offerAmount = 1'230'000'000'000'000'000LL;
3599 auto const takerSeq = env.
seq(taker);
3613 Env env{*
this, features};
3614 env.
fund(
XRP(10'000), issuer, taker);
3618 {.env = env, .issuer = issuer, .holders = {taker}, .transferFee = 10'000}};
3623 std::int64_t const poisonAmount = 8'500'000'000'000'000'000LL;
3624 auto const poisonSeq = env.
seq(issuer);
3629 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3631 auto const takerSeq = env.
seq(taker);
3635 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3641 auto const gwA =
Account(
"gatewayA");
3642 auto const gwB =
Account(
"gatewayB");
3643 auto const alice =
Account(
"alice");
3644 auto const mallory =
Account(
"mallory");
3646 Env env{*
this, features};
3647 env.
fund(
XRP(10'000), gwA, gwB, alice, mallory);
3651 {.env = env, .issuer = gwA, .holders = {alice, mallory}, .transferFee = 50'000}};
3653 MPTTester const tokenB{{.env = env, .issuer = gwB, .holders = {alice, mallory}}};
3655 env(
pay(gwA, alice, tokenA(1'000)));
3661 std::int64_t const poisonPays = 6'148'914'691'236'517'205LL;
3662 std::int64_t const poisonGets = 34'000'000'000'000'000LL;
3663 env(
pay(gwB, mallory, tokenB(poisonGets)));
3665 auto const poisonSeq = env.
seq(mallory);
3666 env(
offer(mallory, tokenA(poisonPays), tokenB(poisonGets)));
3670 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3672 auto const aliceSeq = env.
seq(alice);
3673 env(
offer(alice, tokenB(1), tokenA(100)));
3676 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3682 Env env{*
this, features};
3683 env.
fund(
XRP(10'000), issuer, taker);
3687 {.env = env, .issuer = issuer, .holders = {taker}, .maxAmt =
kMaxMpTokenAmount}};
3701 auto const funded = 1'844'674'407'370'955'162LL;
3702 auto const offerOut = funded + 1;
3704 auto const poisonSeq = env.
seq(issuer);
3709 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3711 auto const issuerXRPBefore = env.
balance(issuer,
XRP);
3712 auto const takerXRPBefore = env.
balance(taker,
XRP);
3714 auto const fee = env.
current()->fees().base;
3716 auto const takerSeq = env.
seq(taker);
3723 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3726 BEAST_EXPECT(env.
balance(issuer,
XRP) == issuerXRPBefore);
3727 BEAST_EXPECT(env.
balance(taker,
XRP) == takerXRPBefore - fee);
3728 BEAST_EXPECT(env.
balance(taker,
token) == takerMPTBefore);
3732 auto const poisonMaker =
Account(
"poisonMaker");
3734 Env env{*
this, features};
3735 env.
fund(
XRP(10'000), issuer, poisonMaker, taker);
3741 .holders = {poisonMaker, taker},
3748 auto const funded = 1'844'674'407'370'955'162LL;
3749 auto const offerOut = funded + 1;
3750 env(
pay(issuer, poisonMaker,
token(funded)));
3752 auto const poisonSeq = env.
seq(poisonMaker);
3756 auto const poisonKeylet =
3758 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3760 auto const takerSeq = env.
seq(taker);
3764 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3785 env.
fund(
XRP(10'000), issuer, taker);
3789 {.env = env, .issuer = issuer, .holders = {taker}, .transferFee = 10'000}};
3791 std::int64_t const poisonAmount = 8'500'000'000'000'000'000LL;
3792 auto const poisonSeq = env.
seq(issuer);
3796 auto const poisonKeylet =
3798 BEAST_EXPECT(env.
le(poisonKeylet) !=
nullptr);
3800 auto const takerSeq = env.
seq(taker);
3804 BEAST_EXPECT(env.
le(poisonKeylet) ==
nullptr);
3808 BEAST_EXPECT(logs.contains(
"Removing offer with overflowing amount calculation"));
5187 using namespace jtx;
5188 Account const issuer(
"issuer");
5192 auto test = [&](
auto&& issue1,
auto&& issue2) {
5193 Env env(*
this, features);
5196 env.
fund(
XRP(1'000), maker, taker);
5200 issue1({.env = env, .token =
"USD", .issuer = issuer, .holders = {maker, taker}});
5202 issue2({.env = env, .token =
"EUR", .issuer = issuer, .holders = {maker, taker}});
5204 env(
pay(issuer, maker, usd(1'000)));
5205 env(
pay(issuer, taker, usd(1'000)));
5206 env(
pay(issuer, maker, eur(1'000)));
5209 auto makerUSDBalance = env.
balance(maker, usd).
value();
5210 auto takerUSDBalance = env.
balance(taker, usd).
value();
5211 auto makerEURBalance = env.
balance(maker, eur).
value();
5212 auto takerEURBalance = env.
balance(taker, eur).
value();
5220 env(
offer(maker,
XRP(100), usd(100)));
5226 makerXRPBalance -=
txFee(env, 1);
5227 takerXRPBalance -=
txFee(env, 1);
5230 makerUSDBalance -= usd(100);
5231 takerUSDBalance += usd(100);
5232 makerXRPBalance +=
XRP(100).value();
5233 takerXRPBalance -=
XRP(100).value();
5237 env(
offer(maker, usd(100),
XRP(100)));
5243 makerXRPBalance -=
txFee(env, 1);
5244 takerXRPBalance -=
txFee(env, 1);
5247 makerUSDBalance += usd(100);
5248 takerUSDBalance -= usd(100);
5249 makerXRPBalance -=
XRP(100).value();
5250 takerXRPBalance +=
XRP(100).value();
5254 env(
offer(maker, usd(100), eur(100)));
5257 env(
offer(taker, eur(100), usd(101)),
Txflags(tfFillOrKill),
Ter(err));
5260 makerXRPBalance -=
txFee(env, 1);
5261 takerXRPBalance -=
txFee(env, 1);
5264 makerUSDBalance += usd(100);
5265 takerUSDBalance -= usd(100);
5266 makerEURBalance -= eur(100);
5267 takerEURBalance += eur(100);
5274 env(
offer(maker,
XRP(101), usd(101)));
5277 env(
offer(taker, usd(100),
XRP(101)),
Txflags(tfFillOrKill | tfSell));
5280 makerUSDBalance -= usd(101);
5281 takerUSDBalance += usd(101);
5282 makerXRPBalance +=
XRP(101).value() -
txFee(env, 1);
5283 takerXRPBalance -=
XRP(101).value() +
txFee(env, 1);
5286 env(
offer(maker, usd(101),
XRP(101)));
5289 env(
offer(taker,
XRP(100), usd(101)),
Txflags(tfFillOrKill | tfSell));
5292 makerUSDBalance += usd(101);
5293 takerUSDBalance -= usd(101);
5294 makerXRPBalance -=
XRP(101).value() +
txFee(env, 1);
5295 takerXRPBalance +=
XRP(101).value() -
txFee(env, 1);
5298 env(
offer(maker, usd(101), eur(101)));
5301 env(
offer(taker, eur(100), usd(101)),
Txflags(tfFillOrKill | tfSell));
5304 makerUSDBalance += usd(101);
5305 takerUSDBalance -= usd(101);
5306 makerEURBalance -= eur(101);
5307 takerEURBalance += eur(101);
5308 makerXRPBalance -=
txFee(env, 1);
5309 takerXRPBalance -=
txFee(env, 1);
5314 for (
auto const flags : {tfFillOrKill, tfFillOrKill + tfSell})
5316 env(
offer(maker,
XRP(100), usd(100)));
5322 makerXRPBalance -=
txFee(env, 1);
5323 takerXRPBalance -=
txFee(env, 1);
5326 env(
offer(maker, usd(100),
XRP(100)));
5332 makerXRPBalance -=
txFee(env, 1);
5333 takerXRPBalance -=
txFee(env, 1);
5336 env(
offer(maker, usd(100), eur(100)));
5342 makerXRPBalance -=
txFee(env, 1);
5343 takerXRPBalance -=
txFee(env, 1);
5348 env.
balance(maker, usd) == makerUSDBalance &&
5349 env.
balance(taker, usd) == takerUSDBalance &&
5350 env.
balance(maker, eur) == makerEURBalance &&
5351 env.
balance(taker, eur) == takerEURBalance &&
5352 env.
balance(maker,
XRP) == makerXRPBalance &&
5363 using namespace jtx;
5365 auto const gw =
Account{
"gateway"};
5366 auto const alice =
Account{
"alice"};
5371 auto const iou = gw[name];
5372 env(
trust(alice, iou(1'000)));
5373 env(
pay(gw, alice, iou(100)));
5379 MPTTester({.env = env, .issuer = gw, .holders = {alice}, .pay = 1'000'000'000});
5396 {.toAsset1 = getIOU, .toAsset2 = getIOU, .val1 = 10, .val2 = 30},
5397 {.toAsset1 = getIOU, .toAsset2 = getXRP, .val1 = 10, .val2 = 30'000'000},
5398 {.toAsset1 = getXRP, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30},
5399 {.toAsset1 = getMPT, .toAsset2 = getXRP, .val1 = 10'000'000, .val2 = 30'000'000},
5400 {.toAsset1 = getXRP, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000},
5401 {.toAsset1 = getIOU, .toAsset2 = getMPT, .val1 = 10, .val2 = 30'000'000},
5402 {.toAsset1 = getMPT, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30},
5403 {.toAsset1 = getMPT, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}};
5404 for (TestInfo
const& t :
tests)
5406 Env env{*
this, features};
5407 env.
fund(
XRP(10'000), gw, alice);
5410 auto const xts = t.toAsset1(env);
5411 auto const xxx = t.toAsset2(env);
5414 return asset.raw().visit(
5415 [&](
Issue const& issue) {
return issue.
native() ?
"XRPIssue" :
"Issue"; },
5416 [&](
MPTIssue const&) {
return "MPTIssue"; });
5419 testcase <<
"offer: " << tokenType(xts) <<
"/" << tokenType(xxx);
5423 auto txn =
noop(gw);
5424 txn[sfTickSize.fieldName] = 5;
5426 BEAST_EXPECT((*env.
le(gw))[sfTickSize] == 5);
5429 env(
offer(alice, xts(t.val1), xxx(t.val2)));
5430 env(
offer(alice, xts(t.val2), xxx(t.val1)));
5431 env(
offer(alice, xts(t.val1), xxx(t.val2)),
Json(jss::Flags, tfSell));
5432 env(
offer(alice, xts(t.val2), xxx(t.val1)),
Json(jss::Flags, tfSell));
5436 if (sle->getType() == ltOFFER)
5440 std::make_pair((*sle)[sfTakerPays], (*sle)[sfTakerGets]));
5445 auto it =
offers.begin();
5446 BEAST_EXPECT(it !=
offers.end());
5447 if (xxx.native() && !xts.holds<
MPTIssue>())
5450 it->second.first == xts(t.val1) && it->second.second ==
XRPAmount(29'999'400));
5452 else if (!xxx.integral())
5455 it->second.first == xts(t.val1) && it->second.second < xxx(t.val2) &&
5456 it->second.second >
STAmount(xxx, 29'9994, -4));
5460 BEAST_EXPECT(it->second.first == xts(t.val1) && it->second.second == xxx(t.val2));
5465 BEAST_EXPECT(it !=
offers.end());
5466 BEAST_EXPECT(it->second.first == xts(t.val2) && it->second.second == xxx(t.val1));
5470 BEAST_EXPECT(it !=
offers.end());
5471 if (xts.native() && !xxx.holds<
MPTIssue>())
5474 it->second.first ==
XRPAmount(10'000'200) && it->second.second == xxx(t.val2));
5476 else if (!xts.integral())
5479 it->second.first ==
STAmount(xts, 10'0002, -4) &&
5480 it->second.second == xxx(t.val2));
5484 BEAST_EXPECT(it->second.first == xts(t.val1) && it->second.second == xxx(t.val2));
5490 BEAST_EXPECT(it !=
offers.end());
5491 BEAST_EXPECT(it->second.first == xts(t.val2) && it->second.second == xxx(t.val1));
5493 BEAST_EXPECT(++it ==
offers.end());