74 testcase(
"exercise partial cross new XRP/IOU offer Q change");
78 auto const gw =
Account{
"gateway"};
79 auto const alice =
Account{
"alice"};
80 auto const bob =
Account{
"bob"};
81 auto const USD = gw[
"USD"];
88 Env env{*
this, features};
91 env.fund(
XRP(10'000'000), gw, alice, bob);
94 env(
trust(alice, USD(10'000'000)));
95 env(
trust(bob, USD(10'000'000)));
98 env(
pay(gw, bob, USD(10'000'000)));
106 auto exerciseOfferPair =
107 [
this, &env, &alice, &bob](
108 Amounts
const& inLedger,
109 Amounts
const& newOffer) ->
unsigned int {
112 env(
offer(alice, inLedger.in, inLedger.out));
116 STAmount const initialRate = Quality(newOffer).rate();
118 STAmount const bobInitialBalance = env.balance(bob);
119 STAmount const bobsFee = env.current()->fees().base;
123 STAmount const bobFinalBalance = env.balance(bob);
127 if (!BEAST_EXPECT(!
offerInLedger(env, alice, aliceOfferSeq)))
133 unsigned int badRate = 1;
139 sfTakerGets, bobOffer[jss::node][sfTakerGets.jsonName]);
141 sfTakerPays, bobOffer[jss::node][sfTakerPays.jsonName]);
143 env.balance(bob) + bobsFee - bobInitialBalance;
144 BEAST_EXPECT(reducedTakerPays < newOffer.in);
145 BEAST_EXPECT(reducedTakerGets < newOffer.out);
147 Quality(Amounts{reducedTakerPays, reducedTakerGets})
150 badRate = inLedgerRate > initialRate ? 1 : 0;
160 reducedTakerPays +
drops(1);
162 Quality(Amounts{tweakedTakerPays, reducedTakerGets})
164 BEAST_EXPECT(tweakedRate > initialRate);
167 std::cout <<
"Placed rate: " << initialRate
168 <<
"; in-ledger rate: " << inLedgerRate
169 <<
"; TakerPays: " << reducedTakerPays
170 <<
"; TakerGets: " << reducedTakerGets
171 <<
"; bob already got: " << bobGot <<
std::endl;
174 inLedgerRate > initialRate ?
"**" :
" ";
175 std::cout <<
"| `" << reducedTakerGets <<
"` | `"
176 << reducedTakerPays <<
"` | `" << initialRate
177 <<
"` | " << filler <<
"`" << inLedgerRate <<
"`"
185 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
190 Amounts
const bobsOffer{
196 unsigned int blockedCount = 0;
198 mantissaReduce <= 5'000'000'000ull;
199 mantissaReduce += 20'000'000ull)
202 bobsOffer.out.
issue(),
203 bobsOffer.out.mantissa() - mantissaReduce,
204 bobsOffer.out.exponent()};
206 bobsOffer.in.
issue(), bobsOffer.in.mantissa() - 1};
207 Amounts alicesOffer{aliceUSD, aliceXRP};
208 blockedCount += exerciseOfferPair(alicesOffer, bobsOffer);
216 if (features[fixReducedOffersV1])
218 BEAST_EXPECT(blockedCount == 0);
222 BEAST_EXPECT(blockedCount >= 170);
230 testcase(
"exercise partial cross old XRP/IOU offer Q change");
234 auto const gw =
Account{
"gateway"};
235 auto const alice =
Account{
"alice"};
236 auto const bob =
Account{
"bob"};
237 auto const USD = gw[
"USD"];
245 Env env{*
this, features};
246 env.fund(
XRP(10'000'000), gw, alice, bob);
249 env(
trust(alice, USD(10'000'000)));
250 env(
trust(bob, USD(10'000'000)));
253 env(
pay(gw, alice, USD(10'000'000)));
260 auto exerciseOfferPair =
261 [
this, &env, &alice, &bob](
262 Amounts
const& inLedger,
263 Amounts
const& newOffer) ->
unsigned int {
266 STAmount const initialRate = Quality(inLedger).rate();
268 env(
offer(alice, inLedger.in, inLedger.out));
273 STAmount const aliceInitialBalance = env.balance(alice);
274 env(
offer(bob, newOffer.in, newOffer.out));
276 STAmount const aliceFinalBalance = env.balance(alice);
284 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
289 unsigned int badRate = 1;
296 aliceOffer[jss::node][sfTakerGets.jsonName]);
299 aliceOffer[jss::node][sfTakerPays.jsonName]);
301 env.balance(alice) - aliceInitialBalance;
302 BEAST_EXPECT(reducedTakerPays < inLedger.in);
303 BEAST_EXPECT(reducedTakerGets < inLedger.out);
305 Quality(Amounts{reducedTakerPays, reducedTakerGets})
307 badRate = inLedgerRate > initialRate ? 1 : 0;
317 reducedTakerPays +
drops(1);
319 Quality(Amounts{tweakedTakerPays, reducedTakerGets})
321 BEAST_EXPECT(tweakedRate > initialRate);
324 std::cout <<
"Placed rate: " << initialRate
325 <<
"; in-ledger rate: " << inLedgerRate
326 <<
"; TakerPays: " << reducedTakerPays
327 <<
"; TakerGets: " << reducedTakerGets
328 <<
"; alice already got: " << aliceGot
332 std::cout <<
"| `" << reducedTakerGets <<
"` | `"
333 << reducedTakerPays <<
"` | `" << initialRate
334 <<
"` | " << filler <<
"`" << inLedgerRate <<
"`"
335 << filler <<
" | `" << aliceGot <<
"` |"
343 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
348 Amounts
const aliceOffer{
354 unsigned int blockedCount = 0;
356 mantissaReduce <= 4'000'000'000ull;
357 mantissaReduce += 20'000'000ull)
360 aliceOffer.out.
issue(),
361 aliceOffer.out.mantissa() - mantissaReduce,
362 aliceOffer.out.exponent()};
364 aliceOffer.in.
issue(), aliceOffer.in.mantissa() - 1};
365 Amounts bobsOffer{bobUSD, bobXRP};
367 blockedCount += exerciseOfferPair(aliceOffer, bobsOffer);
375 if (features[fixReducedOffersV1])
377 BEAST_EXPECT(blockedCount == 0);
381 BEAST_EXPECT(blockedCount > 10);
389 testcase(
"exercise underfunded XRP/IOU offer Q change");
397 auto const alice =
Account{
"alice"};
398 auto const bob =
Account{
"bob"};
400 auto const USD = gw[
"USD"];
407 Env env{*
this, features};
409 env.fund(
XRP(10000), alice, bob, gw);
411 env.trust(USD(1000), alice, bob);
413 int blockedOrderBookCount = 0;
414 for (
STAmount initialBobUSD = USD(0.45); initialBobUSD <= USD(1);
415 initialBobUSD += USD(0.025))
418 env(
pay(gw, bob, initialBobUSD));
437 bool const bobsOfferGone =
439 STAmount const aliceBalanceUSD = env.balance(alice, USD);
442 if (aliceBalanceUSD.
signum() > 0)
444 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
445 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
446 BEAST_EXPECT(bobsOfferGone);
450 if (!bobsOfferGone && aliceBalanceUSD.
signum() == 0)
452 ++blockedOrderBookCount;
458 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
461 if (
STAmount const aliceBalance = env.balance(alice, USD);
462 aliceBalance.signum() > 0)
463 env(
pay(alice, gw, aliceBalance));
465 if (
STAmount const bobBalance = env.balance(bob, USD);
467 env(
pay(bob, gw, bobBalance));
478 if (features[fixReducedOffersV1])
480 BEAST_EXPECT(blockedOrderBookCount == 0);
484 BEAST_EXPECT(blockedOrderBookCount > 15);
492 testcase(
"exercise underfunded IOU/IOU offer Q change");
500 using namespace std::chrono_literals;
501 auto const alice =
Account{
"alice"};
502 auto const bob =
Account{
"bob"};
505 auto const USD = gw[
"USD"];
506 auto const EUR = gw[
"EUR"];
508 STAmount const tinyUSD(USD.issue(), 1, -81);
515 Env env{*
this, features};
517 env.fund(
XRP(10000), alice, bob, gw);
519 env.trust(USD(1000), alice, bob);
520 env.trust(EUR(1000), alice, bob);
523 EUR.issue(), 2957, -76);
525 USD.issue(), 7109, -76);
528 USD.issue(), 50, -81);
530 int blockedOrderBookCount = 0;
531 for (
STAmount initialBobUSD = tinyUSD; initialBobUSD <= endLoop;
532 initialBobUSD += tinyUSD)
535 env(
pay(gw, bob, initialBobUSD));
536 env(
pay(gw, alice, EUR(100)));
541 env(
offer(bob, eurOffer, usdOffer));
543 env.require(
offers(bob, 1));
547 env(
offer(alice, usdOffer, eurOffer));
552 bool const bobsOfferGone =
554 STAmount aliceBalanceUSD = env.balance(alice, USD);
557 <<
"bobs initial: " << initialBobUSD
558 <<
"; alice final: " << aliceBalanceUSD
559 <<
"; bobs offer: " << bobsOfferJson.toStyledString()
563 if (aliceBalanceUSD.
signum() > 0)
565 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
566 BEAST_EXPECT(env.balance(bob, USD) == USD(0));
567 BEAST_EXPECT(bobsOfferGone);
571 if (!bobsOfferGone && aliceBalanceUSD.
signum() == 0)
573 ++blockedOrderBookCount;
580 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}});
583 auto zeroBalance = [&env, &gw](
590 zeroBalance(alice, EUR);
591 zeroBalance(alice, USD);
592 zeroBalance(bob, EUR);
593 zeroBalance(bob, USD);
602 if (features[fixReducedOffersV1])
604 BEAST_EXPECT(blockedOrderBookCount == 0);
608 BEAST_EXPECT(blockedOrderBookCount > 20);
630 testcase(
"exercise tfSell partial cross old XRP/IOU offer Q change");
638 auto const USD = gw[
"USD"];
646 Env env{*
this, features};
647 env.fund(
XRP(10'000'000), gw, alice, bob, carol);
650 env(
trust(alice, USD(10'000'000)));
651 env(
trust(bob, USD(10'000'000)));
652 env(
trust(carol, USD(10'000'000)));
655 env(
pay(gw, alice, USD(10'000'000)));
656 env(
pay(gw, bob, USD(10'000'000)));
657 env(
pay(gw, carol, USD(10'000'000)));
664 auto exerciseOfferTrio =
665 [
this, &env, &alice, &bob, &carol, &USD](
666 Amounts
const& carolOffer) ->
unsigned int {
669 static Amounts
const aliceInitialOffer(USD(2),
drops(3382562));
670 env(
offer(alice, aliceInitialOffer.in, aliceInitialOffer.out));
674 env, alice, aliceOfferSeq)[jss::node]))
679 env(
offer(bob, USD(0.97086565812384),
drops(1642020)));
685 env(
offer(carol, carolOffer.in, carolOffer.out),
699 {{alice, aliceOfferSeq},
701 {carol, carolOfferSeq}});
706 unsigned int badRate = 1;
711 Amounts aliceReducedOffer =
714 BEAST_EXPECT(aliceReducedOffer.in < aliceInitialOffer.in);
715 BEAST_EXPECT(aliceReducedOffer.out < aliceInitialOffer.out);
717 Quality(aliceReducedOffer).rate();
718 badRate = inLedgerRate > initialRate ? 1 : 0;
728 aliceReducedOffer.in.issue(),
729 aliceReducedOffer.in.mantissa() + 1,
730 aliceReducedOffer.in.exponent(),
731 aliceReducedOffer.in.negative());
734 Amounts{aliceReducedOffer.in, tweakedTakerGets})
736 BEAST_EXPECT(tweakedRate > initialRate);
739 std::cout <<
"Placed rate: " << initialRate
740 <<
"; in-ledger rate: " << inLedgerRate
741 <<
"; TakerPays: " << aliceReducedOffer.in
742 <<
"; TakerGets: " << aliceReducedOffer.out
746 std::cout <<
"| " << aliceReducedOffer.in <<
"` | `"
747 << aliceReducedOffer.out <<
"` | `" << initialRate
748 <<
"` | " << filler <<
"`" << inLedgerRate <<
"`"
757 {{alice, aliceOfferSeq},
759 {carol, carolOfferSeq}});
763 constexpr int loopCount = 100;
764 unsigned int blockedCount = 0;
768 for (
unsigned int i = 0; i < loopCount; ++i)
770 blockedCount += exerciseOfferTrio(
771 Amounts(
drops(1642020), USD(1) + increaseGets));
772 increaseGets += step;
781 if (features[fixReducedOffersV2])
783 BEAST_EXPECT(blockedCount == 0);
787 BEAST_EXPECT(blockedCount > 80);