2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h>
6#include <test/jtx/fee.h>
7#include <test/jtx/offer.h>
8#include <test/jtx/owners.h>
9#include <test/jtx/pay.h>
10#include <test/jtx/trust.h>
11#include <test/jtx/txflags.h>
13#include <xrpl/beast/unit_test/suite.h>
14#include <xrpl/json/json_value.h>
15#include <xrpl/json/to_string.h>
16#include <xrpl/protocol/Feature.h>
17#include <xrpl/protocol/Quality.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STAmount.h>
20#include <xrpl/protocol/TxFlags.h>
21#include <xrpl/protocol/jss.h>
35 jvParams[jss::offer][jss::account] = acct.
human();
36 jvParams[jss::offer][jss::seq] = offerSeq;
37 return env.
rpc(
"json",
"ledger_entry",
to_string(jvParams))[jss::result];
46 ledgerOffer[jss::error].asString() ==
"entryNotFound");
55 for (
auto [acct, offerSeq] : list)
64 testcase(
"exercise partial cross new XRP/IOU offer Q change");
68 auto const gw =
Account{
"gateway"};
69 auto const alice =
Account{
"alice"};
70 auto const bob =
Account{
"bob"};
71 auto const usd = gw[
"USD"];
77 env.
fund(
XRP(10'000'000), gw, alice, bob);
80 env(
trust(alice, usd(10'000'000)));
81 env(
trust(bob, usd(10'000'000)));
84 env(
pay(gw, bob, usd(10'000'000)));
92 auto exerciseOfferPair = [
this, &env, &alice, &bob](
94 Amounts const& newOffer) ->
unsigned int {
97 env(
offer(alice, inLedger.in, inLedger.out));
105 env(
offer(bob, newOffer.in, newOffer.out, tfSell),
Fee(bobFee));
111 if (!BEAST_EXPECT(!
offerInLedger(env, alice, aliceOfferSeq)))
119 unsigned int badRate = 1;
124 amountFromJson(sfTakerGets, bobOffer[jss::node][sfTakerGets.jsonName]);
126 amountFromJson(sfTakerPays, bobOffer[jss::node][sfTakerPays.jsonName]);
128 BEAST_EXPECT(reducedTakerPays < newOffer.in);
129 BEAST_EXPECT(reducedTakerGets < newOffer.out);
133 badRate = inLedgerRate > initialRate ? 1 : 0;
142 STAmount const tweakedTakerPays = reducedTakerPays +
drops(1);
145 BEAST_EXPECT(tweakedRate > initialRate);
161 unsigned int blockedCount = 0;
163 mantissaReduce <= 5'000'000'000ull;
164 mantissaReduce += 20'000'000ull)
171 Amounts const aliceOffer{aliceUSD, aliceXRP};
172 blockedCount += exerciseOfferPair(aliceOffer, bobOffer);
177 BEAST_EXPECT(blockedCount == 0);
184 testcase(
"exercise partial cross old XRP/IOU offer Q change");
188 auto const gw =
Account{
"gateway"};
189 auto const alice =
Account{
"alice"};
190 auto const bob =
Account{
"bob"};
191 auto const usd = gw[
"USD"];
196 env.
fund(
XRP(10'000'000), gw, alice, bob);
199 env(
trust(alice, usd(10'000'000)));
200 env(
trust(bob, usd(10'000'000)));
203 env(
pay(gw, alice, usd(10'000'000)));
210 auto exerciseOfferPair = [
this, &env, &alice, &bob](
212 Amounts const& newOffer) ->
unsigned int {
217 env(
offer(alice, inLedger.in, inLedger.out));
223 env(
offer(bob, newOffer.in, newOffer.out));
237 unsigned int badRate = 1;
242 amountFromJson(sfTakerGets, aliceOffer[jss::node][sfTakerGets.jsonName]);
244 amountFromJson(sfTakerPays, aliceOffer[jss::node][sfTakerPays.jsonName]);
246 BEAST_EXPECT(reducedTakerPays < inLedger.in);
247 BEAST_EXPECT(reducedTakerGets < inLedger.out);
250 badRate = inLedgerRate > initialRate ? 1 : 0;
259 STAmount const tweakedTakerPays = reducedTakerPays +
drops(1);
262 BEAST_EXPECT(tweakedRate > initialRate);
278 unsigned int blockedCount = 0;
280 mantissaReduce <= 4'000'000'000ull;
281 mantissaReduce += 20'000'000ull)
288 Amounts const bobOffer{bobUSD, bobXRP};
290 blockedCount += exerciseOfferPair(aliceOffer, bobOffer);
295 BEAST_EXPECT(blockedCount == 0);
302 testcase(
"exercise underfunded XRP/IOU offer Q change");
307 auto const alice =
Account{
"alice"};
308 auto const bob =
Account{
"bob"};
310 auto const usd = gw[
"USD"];
315 env.
fund(
XRP(10000), alice, bob, gw);
317 env.
trust(usd(1000), alice, bob);
319 int blockedOrderBookCount = 0;
320 for (
STAmount initialBobUSD = usd(0.45); initialBobUSD <= usd(1);
321 initialBobUSD += usd(0.025))
324 env(
pay(gw, bob, initialBobUSD));
343 bool const bobOfferGone = !
offerInLedger(env, bob, bobOfferSeq);
347 if (aliceBalanceUSD.
signum() > 0)
349 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
350 BEAST_EXPECT(env.
balance(bob, usd) == usd(0));
351 BEAST_EXPECT(bobOfferGone);
355 if (!bobOfferGone && aliceBalanceUSD.
signum() == 0)
357 ++blockedOrderBookCount;
366 aliceBalance.
signum() > 0)
367 env(
pay(alice, gw, aliceBalance));
370 env(
pay(bob, gw, bobBalance));
378 BEAST_EXPECT(blockedOrderBookCount == 0);
385 testcase(
"exercise underfunded IOU/IOU offer Q change");
390 using namespace std::chrono_literals;
391 auto const alice =
Account{
"alice"};
392 auto const bob =
Account{
"bob"};
395 auto const usd = gw[
"USD"];
396 auto const eur = gw[
"EUR"];
398 STAmount const tinyUSD(usd, 1, -81);
403 env.
fund(
XRP(10000), alice, bob, gw);
405 env.
trust(usd(1000), alice, bob);
406 env.
trust(eur(1000), alice, bob);
408 STAmount const eurOffer(eur, 2957, -76);
409 STAmount const usdOffer(usd, 7109, -76);
411 STAmount const endLoop(usd, 50, -81);
413 int blockedOrderBookCount = 0;
414 for (
STAmount initialBobUSD = tinyUSD; initialBobUSD <= endLoop;
415 initialBobUSD += tinyUSD)
418 env(
pay(gw, bob, initialBobUSD));
419 env(
pay(gw, alice, eur(100)));
424 env(
offer(bob, eurOffer, usdOffer));
430 env(
offer(alice, usdOffer, eurOffer));
435 bool const bobOfferGone = !
offerInLedger(env, bob, bobOfferSeq);
438 if (aliceBalanceUSD.
signum() > 0)
440 BEAST_EXPECT(aliceBalanceUSD == initialBobUSD);
441 BEAST_EXPECT(env.
balance(bob, usd) == usd(0));
442 BEAST_EXPECT(bobOfferGone);
446 if (!bobOfferGone && aliceBalanceUSD.
signum() == 0)
448 ++blockedOrderBookCount;
457 auto zeroBalance = [&env, &gw](
Account const& acct,
IOU const& iou) {
459 env(
pay(acct, gw, balance));
462 zeroBalance(alice, eur);
463 zeroBalance(alice, usd);
464 zeroBalance(bob, eur);
465 zeroBalance(bob, usd);
471 BEAST_EXPECT(blockedOrderBookCount == 0);
490 testcase(
"exercise tfSell partial cross old XRP/IOU offer Q change");
498 auto const usd = gw[
"USD"];
505 Env env{*
this, features};
506 env.
fund(
XRP(10'000'000), gw, alice, bob, carol);
509 env(
trust(alice, usd(10'000'000)));
510 env(
trust(bob, usd(10'000'000)));
511 env(
trust(carol, usd(10'000'000)));
514 env(
pay(gw, alice, usd(10'000'000)));
515 env(
pay(gw, bob, usd(10'000'000)));
516 env(
pay(gw, carol, usd(10'000'000)));
523 auto exerciseOfferTrio = [
this, &env, &alice, &bob, &carol, &usd](
524 Amounts const& carolOffer) ->
unsigned int {
527 static Amounts const kAliceInitialOffer(usd(2),
drops(3382562));
528 env(
offer(alice, kAliceInitialOffer.
in, kAliceInitialOffer.
out));
531 env, alice, aliceOfferSeq)[jss::node]))
536 env(
offer(bob, usd(0.97086565812384),
drops(1642020)));
542 env(
offer(carol, carolOffer.in, carolOffer.out),
Txflags(tfSell));
554 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}});
559 unsigned int badRate = 1;
565 BEAST_EXPECT(aliceReducedOffer.
in < kAliceInitialOffer.
in);
566 BEAST_EXPECT(aliceReducedOffer.
out < kAliceInitialOffer.
out);
568 badRate = inLedgerRate > initialRate ? 1 : 0;
584 BEAST_EXPECT(tweakedRate > initialRate);
591 env, {{alice, aliceOfferSeq}, {bob, bobOfferSeq}, {carol, carolOfferSeq}});
595 static constexpr int kLoopCount = 100;
596 unsigned int blockedCount = 0;
600 for (
unsigned int i = 0; i < kLoopCount; ++i)
603 exerciseOfferTrio(
Amounts(
drops(1642020), usd(1) + increaseGets));
604 increaseGets += step;
613 if (features[fixReducedOffersV2])
615 BEAST_EXPECT(blockedCount == 0);
619 BEAST_EXPECT(blockedCount > 80);
TestcaseT testcase
Memberspace for declaring test cases.
bool isMember(char const *key) const
Return true if the object has a member named key.
Represents the logical ratio of output currency to input currency.
STAmount rate() const
Returns the quality as STAmount.
std::uint64_t mantissa() const noexcept
int signum() const noexcept
bool negative() const noexcept
Asset const & asset() const
int exponent() const noexcept
static auto ledgerEntryOffer(jtx::Env &env, jtx::Account const &acct, std::uint32_t offerSeq)
void testPartialCrossNewXrpIouQChange()
void testSellPartialCrossOldXrpIouQChange()
void testUnderFundedIouIouQChange()
void run() override
Runs the suite.
static bool offerInLedger(jtx::Env &env, jtx::Account const &acct, std::uint32_t offerSeq)
static Amounts jsonOfferToAmounts(json::Value const &json)
void testUnderFundedXrpIouQChange()
static void cleanupOldOffers(jtx::Env &env, std::initializer_list< std::pair< jtx::Account const &, std::uint32_t > > list)
void testPartialCrossOldXrpIouQChange()
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
void require(Args const &... args)
Check a set of requirements.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Converts to IOU Issue or STAmount.
JSON (JavaScript Object Notation).
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
json::Value offerCancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
XrpT const XRP
Converts to XRP Issue or STAmount.
FeatureBitset testableAmendments()
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
OwnerCount< ltOFFER > offers
Match the number of offers in the account's owner directory.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, xrpl, 2)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
TAmounts< STAmount, STAmount > Amounts
std::string to_string(BaseUInt< Bits, Tag > const &a)
STAmount amountFromJson(SField const &name, json::Value const &v)