xrpld
Loading...
Searching...
No Matches
Flow_test.cpp
1#include <test/jtx/Account.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/PathSet.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/balance.h>
7#include <test/jtx/offer.h>
8#include <test/jtx/owners.h>
9#include <test/jtx/paths.h>
10#include <test/jtx/pay.h>
11#include <test/jtx/quality.h>
12#include <test/jtx/rate.h>
13#include <test/jtx/sendmax.h>
14#include <test/jtx/ter.h>
15#include <test/jtx/ticket.h>
16#include <test/jtx/trust.h>
17#include <test/jtx/txflags.h>
18
19#include <xrpl/basics/base_uint.h>
20#include <xrpl/basics/contract.h>
21#include <xrpl/beast/unit_test/suite.h>
22#include <xrpl/core/ServiceRegistry.h>
23#include <xrpl/ledger/ApplyView.h>
24#include <xrpl/ledger/OpenView.h>
25#include <xrpl/ledger/PaymentSandbox.h>
26#include <xrpl/ledger/Sandbox.h>
27#include <xrpl/ledger/helpers/DirectoryHelpers.h>
28#include <xrpl/ledger/helpers/OfferHelpers.h>
29#include <xrpl/protocol/AccountID.h>
30#include <xrpl/protocol/Feature.h>
31#include <xrpl/protocol/Indexes.h>
32#include <xrpl/protocol/Issue.h>
33#include <xrpl/protocol/Keylet.h>
34#include <xrpl/protocol/LedgerFormats.h>
35#include <xrpl/protocol/SField.h>
36#include <xrpl/protocol/STAmount.h>
37#include <xrpl/protocol/STPathSet.h>
38#include <xrpl/protocol/TER.h>
39#include <xrpl/protocol/TxFlags.h>
40#include <xrpl/protocol/UintTypes.h>
41#include <xrpl/protocol/XRPAmount.h>
42#include <xrpl/tx/paths/Flow.h>
43#include <xrpl/tx/paths/detail/Steps.h>
44
45#include <cstdint>
46#include <optional>
47#include <stdexcept>
48#include <string>
49#include <vector>
50
51namespace xrpl::test {
52
53bool
55 jtx::Env const& env,
56 jtx::Account const& src,
57 jtx::Account const& dst,
58 Currency const& cur)
59{
60 if (auto sle = env.le(keylet::trustLine(src, dst, cur)))
61 {
62 auto const flag = (src.id() > dst.id()) ? lsfHighNoRipple : lsfLowNoRipple;
63 return sle->isFlag(flag);
64 }
65 Throw<std::runtime_error>("No line in getTrustFlag");
66 return false; // silence warning
67}
68
70{
71 void
73 {
74 testcase("Direct Step");
75
76 using namespace jtx;
77 auto const alice = Account("alice");
78 auto const bob = Account("bob");
79 auto const carol = Account("carol");
80 auto const dan = Account("dan");
81 auto const erin = Account("erin");
82 auto const usda = alice["USD"];
83 auto const usdb = bob["USD"];
84 auto const usdc = carol["USD"];
85 auto const usdd = dan["USD"];
86 auto const gw = Account("gw");
87 auto const usd = gw["USD"];
88 {
89 // Pay USD, trivial path
90 Env env(*this, features);
91
92 env.fund(XRP(10000), alice, bob, gw);
93 env.close();
94 env.trust(usd(1000), alice, bob);
95 env(pay(gw, alice, usd(100)));
96 env(pay(alice, bob, usd(10)), Paths(usd));
97 env.require(Balance(bob, usd(10)));
98 }
99 {
100 // XRP transfer
101 Env env(*this, features);
102
103 env.fund(XRP(10000), alice, bob);
104 env.close();
105 env(pay(alice, bob, XRP(100)));
106 env.require(Balance(bob, XRP(10000 + 100)));
107 env.require(Balance(alice, xrpMinusFee(env, 10000 - 100)));
108 }
109 {
110 // Partial payments
111 Env env(*this, features);
112
113 env.fund(XRP(10000), alice, bob, gw);
114 env.close();
115 env.trust(usd(1000), alice, bob);
116 env(pay(gw, alice, usd(100)));
117 env(pay(alice, bob, usd(110)), Paths(usd), Ter(tecPATH_PARTIAL));
118 env.require(Balance(bob, usd(0)));
119 env(pay(alice, bob, usd(110)), Paths(usd), Txflags(tfPartialPayment));
120 env.require(Balance(bob, usd(100)));
121 }
122 {
123 // Pay by rippling through accounts, use path finder
124 Env env(*this, features);
125
126 env.fund(XRP(10000), alice, bob, carol, dan);
127 env.close();
128 env.trust(usda(10), bob);
129 env.trust(usdb(10), carol);
130 env.trust(usdc(10), dan);
131 env(pay(alice, dan, usdc(10)), Paths(usda));
132 env.require(Balance(bob, usda(10)), Balance(carol, usdb(10)), Balance(dan, usdc(10)));
133 }
134 {
135 // Pay by rippling through accounts, specify path
136 // and charge a transfer fee
137 Env env(*this, features);
138
139 env.fund(XRP(10000), alice, bob, carol, dan);
140 env.close();
141 env.trust(usda(10), bob);
142 env.trust(usdb(10), alice, carol);
143 env.trust(usdc(10), dan);
144 env(rate(bob, 1.1));
145
146 // alice will redeem to bob; a transfer fee will be charged
147 env(pay(bob, alice, usdb(6)));
148 env(pay(alice, dan, usdc(5)),
149 Path(bob, carol),
150 Sendmax(usda(6)),
151 Txflags(tfNoRippleDirect));
152 env.require(Balance(dan, usdc(5)));
153 env.require(Balance(alice, usdb(0.5)));
154 }
155 {
156 // Pay by rippling through accounts, specify path and transfer fee
157 // Test that the transfer fee is not charged when alice issues
158 Env env(*this, features);
159
160 env.fund(XRP(10000), alice, bob, carol, dan);
161 env.close();
162 env.trust(usda(10), bob);
163 env.trust(usdb(10), alice, carol);
164 env.trust(usdc(10), dan);
165 env(rate(bob, 1.1));
166
167 env(pay(alice, dan, usdc(5)),
168 Path(bob, carol),
169 Sendmax(usda(6)),
170 Txflags(tfNoRippleDirect));
171 env.require(Balance(dan, usdc(5)));
172 env.require(Balance(bob, usda(5)));
173 }
174 {
175 // test best quality path is taken
176 // Paths: A->B->D->E ; A->C->D->E
177 Env env(*this, features);
178
179 env.fund(XRP(10000), alice, bob, carol, dan, erin);
180 env.close();
181 env.trust(usda(10), bob, carol);
182 env.trust(usdb(10), dan);
183 env.trust(usdc(10), alice, dan);
184 env.trust(usdd(20), erin);
185 env(rate(bob, 1));
186 env(rate(carol, 1.1));
187
188 // Pay alice so she redeems to carol and a transfer fee is charged
189 env(pay(carol, alice, usdc(10)));
190 env(pay(alice, erin, usdd(5)),
191 Path(carol, dan),
192 Path(bob, dan),
193 Txflags(tfNoRippleDirect));
194
195 env.require(Balance(erin, usdd(5)));
196 env.require(Balance(dan, usdb(5)));
197 env.require(Balance(dan, usdc(0)));
198 }
199 {
200 // Limit quality
201 Env env(*this, features);
202
203 env.fund(XRP(10000), alice, bob, carol);
204 env.close();
205 env.trust(usda(10), bob);
206 env.trust(usdb(10), carol);
207
208 env(pay(alice, carol, usdb(5)),
209 Sendmax(usda(4)),
210 Txflags(tfLimitQuality | tfPartialPayment),
212 env.require(Balance(carol, usdb(0)));
213
214 env(pay(alice, carol, usdb(5)), Sendmax(usda(4)), Txflags(tfPartialPayment));
215 env.require(Balance(carol, usdb(4)));
216 }
217 }
218
219 void
221 {
222 testcase("Line Quality");
223
224 using namespace jtx;
225 auto const alice = Account("alice");
226 auto const bob = Account("bob");
227 auto const carol = Account("carol");
228 auto const dan = Account("dan");
229 auto const usda = alice["USD"];
230 auto const usdb = bob["USD"];
231 auto const usdc = carol["USD"];
232 auto const usdd = dan["USD"];
233
234 // Dan -> Bob -> Alice -> Carol; vary bobDanQIn and bobAliceQOut
235 for (auto bobDanQIn : {80, 100, 120})
236 {
237 for (auto bobAliceQOut : {80, 100, 120})
238 {
239 Env env(*this, features);
240 env.fund(XRP(10000), alice, bob, carol, dan);
241 env.close();
242 env(trust(bob, usdd(100)), QualityInPercent(bobDanQIn));
243 env(trust(bob, usda(100)), QualityOutPercent(bobAliceQOut));
244 env(trust(carol, usda(100)));
245
246 env(pay(alice, bob, usda(100)));
247 env.require(Balance(bob, usda(100)));
248 env(pay(dan, carol, usda(10)),
249 Path(bob),
250 Sendmax(usdd(100)),
251 Txflags(tfNoRippleDirect));
252 env.require(Balance(bob, usda(90)));
253 if (bobAliceQOut > bobDanQIn)
254 {
255 env.require(
256 Balance(bob, usdd(10.0 * double(bobAliceQOut) / double(bobDanQIn))));
257 }
258 else
259 {
260 env.require(Balance(bob, usdd(10)));
261 }
262 env.require(Balance(carol, usda(10)));
263 }
264 }
265
266 // bob -> alice -> carol; vary carolAliceQIn
267 for (auto carolAliceQIn : {80, 100, 120})
268 {
269 Env env(*this, features);
270 env.fund(XRP(10000), alice, bob, carol);
271 env.close();
272
273 env(trust(bob, usda(10)));
274 env(trust(carol, usda(10)), QualityInPercent(carolAliceQIn));
275
276 env(pay(alice, bob, usda(10)));
277 env.require(Balance(bob, usda(10)));
278 env(pay(bob, carol, usda(5)), Sendmax(usda(10)));
279 auto const effectiveQ = carolAliceQIn > 100 ? 1.0 : carolAliceQIn / 100.0;
280 env.require(Balance(bob, usda(10.0 - (5.0 / effectiveQ))));
281 }
282
283 // bob -> alice -> carol; bobAliceQOut varies.
284 for (auto bobAliceQOut : {80, 100, 120})
285 {
286 Env env(*this, features);
287 env.fund(XRP(10000), alice, bob, carol);
288 env.close();
289 env(trust(bob, usda(10)), QualityOutPercent(bobAliceQOut));
290 env(trust(carol, usda(10)));
291
292 env(pay(alice, bob, usda(10)));
293 env.require(Balance(bob, usda(10)));
294 env(pay(bob, carol, usda(5)), Sendmax(usda(5)));
295 env.require(Balance(carol, usda(5)));
296 env.require(Balance(bob, usda(10 - 5)));
297 }
298 }
299
300 void
302 {
303 testcase("Book Step");
304
305 using namespace jtx;
306
307 auto const gw = Account("gateway");
308 auto const usd = gw["USD"];
309 auto const btc = gw["BTC"];
310 auto const eur = gw["EUR"];
311 Account const alice("alice");
312 Account const bob("bob");
313 Account const carol("carol");
314
315 {
316 // simple IOU/IOU offer
317 Env env(*this, features);
318
319 env.fund(XRP(10000), alice, bob, carol, gw);
320 env.close();
321 env.trust(usd(1000), alice, bob, carol);
322 env.trust(btc(1000), alice, bob, carol);
323
324 env(pay(gw, alice, btc(50)));
325 env(pay(gw, bob, usd(50)));
326
327 env(offer(bob, btc(50), usd(50)));
328
329 env(pay(alice, carol, usd(50)), Path(~usd), Sendmax(btc(50)));
330
331 env.require(Balance(alice, btc(0)));
332 env.require(Balance(bob, btc(50)));
333 env.require(Balance(bob, usd(0)));
334 env.require(Balance(carol, usd(50)));
335 BEAST_EXPECT(!isOffer(env, bob, btc(50), usd(50)));
336 }
337 {
338 // simple IOU/XRP XRP/IOU offer
339 Env env(*this, features);
340
341 env.fund(XRP(10000), alice, bob, carol, gw);
342 env.close();
343 env.trust(usd(1000), alice, bob, carol);
344 env.trust(btc(1000), alice, bob, carol);
345
346 env(pay(gw, alice, btc(50)));
347 env(pay(gw, bob, usd(50)));
348
349 env(offer(bob, btc(50), XRP(50)));
350 env(offer(bob, XRP(50), usd(50)));
351
352 env(pay(alice, carol, usd(50)), Path(~XRP, ~usd), Sendmax(btc(50)));
353
354 env.require(Balance(alice, btc(0)));
355 env.require(Balance(bob, btc(50)));
356 env.require(Balance(bob, usd(0)));
357 env.require(Balance(carol, usd(50)));
358 BEAST_EXPECT(!isOffer(env, bob, XRP(50), usd(50)));
359 BEAST_EXPECT(!isOffer(env, bob, btc(50), XRP(50)));
360 }
361 {
362 // simple XRP -> USD through offer and sendmax
363 Env env(*this, features);
364
365 env.fund(XRP(10000), alice, bob, carol, gw);
366 env.close();
367 env.trust(usd(1000), alice, bob, carol);
368 env.trust(btc(1000), alice, bob, carol);
369
370 env(pay(gw, bob, usd(50)));
371
372 env(offer(bob, XRP(50), usd(50)));
373
374 env(pay(alice, carol, usd(50)), Path(~usd), Sendmax(XRP(50)));
375
376 env.require(Balance(alice, xrpMinusFee(env, 10000 - 50)));
377 env.require(Balance(bob, xrpMinusFee(env, 10000 + 50)));
378 env.require(Balance(bob, usd(0)));
379 env.require(Balance(carol, usd(50)));
380 BEAST_EXPECT(!isOffer(env, bob, XRP(50), usd(50)));
381 }
382 {
383 // simple USD -> XRP through offer and sendmax
384 Env env(*this, features);
385
386 env.fund(XRP(10000), alice, bob, carol, gw);
387 env.close();
388 env.trust(usd(1000), alice, bob, carol);
389 env.trust(btc(1000), alice, bob, carol);
390
391 env(pay(gw, alice, usd(50)));
392
393 env(offer(bob, usd(50), XRP(50)));
394
395 env(pay(alice, carol, XRP(50)), Path(~XRP), Sendmax(usd(50)));
396
397 env.require(Balance(alice, usd(0)));
398 env.require(Balance(bob, xrpMinusFee(env, 10000 - 50)));
399 env.require(Balance(bob, usd(50)));
400 env.require(Balance(carol, XRP(10000 + 50)));
401 BEAST_EXPECT(!isOffer(env, bob, usd(50), XRP(50)));
402 }
403 {
404 // test unfunded offers are removed when payment succeeds
405 Env env(*this, features);
406
407 env.fund(XRP(10000), alice, bob, carol, gw);
408 env.close();
409 env.trust(usd(1000), alice, bob, carol);
410 env.trust(btc(1000), alice, bob, carol);
411 env.trust(eur(1000), alice, bob, carol);
412
413 env(pay(gw, alice, btc(60)));
414 env(pay(gw, bob, usd(50)));
415 env(pay(gw, bob, eur(50)));
416
417 env(offer(bob, btc(50), usd(50)));
418 env(offer(bob, btc(40), eur(50)));
419 env(offer(bob, eur(50), usd(50)));
420
421 // unfund offer
422 env(pay(bob, gw, eur(50)));
423 BEAST_EXPECT(isOffer(env, bob, btc(50), usd(50)));
424 BEAST_EXPECT(isOffer(env, bob, btc(40), eur(50)));
425 BEAST_EXPECT(isOffer(env, bob, eur(50), usd(50)));
426
427 env(pay(alice, carol, usd(50)), Path(~usd), Path(~eur, ~usd), Sendmax(btc(60)));
428
429 env.require(Balance(alice, btc(10)));
430 env.require(Balance(bob, btc(50)));
431 env.require(Balance(bob, usd(0)));
432 env.require(Balance(bob, eur(0)));
433 env.require(Balance(carol, usd(50)));
434 // used in the payment
435 BEAST_EXPECT(!isOffer(env, bob, btc(50), usd(50)));
436 // found unfunded
437 BEAST_EXPECT(!isOffer(env, bob, btc(40), eur(50)));
438 // unfunded, but should not yet be found unfunded
439 BEAST_EXPECT(isOffer(env, bob, eur(50), usd(50)));
440 }
441 {
442 // test unfunded offers are returned when the payment fails.
443 // bob makes two offers: a funded 50 USD for 50 BTC and an unfunded
444 // 50 EUR for 60 BTC. alice pays carol 61 USD with 61 BTC. alice
445 // only has 60 BTC, so the payment will fail. The payment uses two
446 // paths: one through bob's funded offer and one through his
447 // unfunded offer. When the payment fails `flow` should return the
448 // unfunded offer. This test is intentionally similar to the one
449 // that removes unfunded offers when the payment succeeds.
450 Env env(*this, features);
451
452 env.fund(XRP(10000), alice, bob, carol, gw);
453 env.close();
454 env.trust(usd(1000), alice, bob, carol);
455 env.trust(btc(1000), alice, bob, carol);
456 env.trust(eur(1000), alice, bob, carol);
457
458 env(pay(gw, alice, btc(60)));
459 env(pay(gw, bob, usd(60)));
460 env(pay(gw, bob, eur(50)));
461 env(pay(gw, carol, eur(1)));
462
463 env(offer(bob, btc(50), usd(50)));
464 env(offer(bob, btc(60), eur(50)));
465 env(offer(carol, btc(1000), eur(1)));
466 env(offer(bob, eur(50), usd(50)));
467
468 // unfund offer
469 env(pay(bob, gw, eur(50)));
470 BEAST_EXPECT(isOffer(env, bob, btc(50), usd(50)));
471 BEAST_EXPECT(isOffer(env, bob, btc(60), eur(50)));
472 BEAST_EXPECT(isOffer(env, carol, btc(1000), eur(1)));
473
474 auto flowJournal = env.app().getJournal("Flow");
475 auto const flowResult = [&] {
476 STAmount const deliver(usd(51));
477 STAmount smax(btc(61));
478 PaymentSandbox sb(env.current().get(), TapNone);
479 STPathSet paths;
480 auto ipe = [](Issue const& iss) {
481 return STPathElement(
483 xrpAccount(),
484 iss.currency,
485 iss.account);
486 };
487 {
488 // BTC -> USD
489 STPath const p1({ipe(usd)});
490 paths.pushBack(p1);
491 // BTC -> EUR -> USD
492 STPath const p2({ipe(eur), ipe(usd)});
493 paths.pushBack(p2);
494 }
495
496 return flow(
497 sb,
498 deliver,
499 alice,
500 carol,
501 paths,
502 false,
503 false,
504 true,
506 std::nullopt,
507 smax,
508 std::nullopt,
509 flowJournal);
510 }();
511
512 BEAST_EXPECT(flowResult.removableOffers.size() == 1);
513 env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
514 if (flowResult.removableOffers.empty())
515 return false;
516 Sandbox sb(&view, TapNone);
517 for (auto const& o : flowResult.removableOffers)
518 {
519 if (auto ok = sb.peek(keylet::offer(o)))
520 offerDelete(sb, ok, flowJournal);
521 }
522 sb.apply(view);
523 return true;
524 });
525
526 // used in payment, but since payment failed should be untouched
527 BEAST_EXPECT(isOffer(env, bob, btc(50), usd(50)));
528 BEAST_EXPECT(isOffer(env, carol, btc(1000), eur(1)));
529 // found unfunded
530 BEAST_EXPECT(!isOffer(env, bob, btc(60), eur(50)));
531 }
532 {
533 // Do not produce more in the forward pass than the reverse pass
534 // This test uses a path that whose reverse pass will compute a
535 // 0.5 USD input required for a 1 EUR output. It sets a sendmax of
536 // 0.4 USD, so the payment engine will need to do a forward pass.
537 // Without limits, the 0.4 USD would produce 1000 EUR in the forward
538 // pass. This test checks that the payment produces 1 EUR, as
539 // expected.
540 Env env(*this, features);
541 env.fund(XRP(10000), alice, bob, carol, gw);
542 env.close();
543
544 env.trust(usd(1000), alice, bob, carol);
545 env.trust(eur(1000), alice, bob, carol);
546
547 env(pay(gw, alice, usd(1000)));
548 env(pay(gw, bob, eur(1000)));
549
550 Keylet const bobUsdOffer = keylet::offer(bob, env.seq(bob));
551 env(offer(bob, usd(1), drops(2)), Txflags(tfPassive));
552 env(offer(bob, drops(1), eur(1000)), Txflags(tfPassive));
553
554 bool const reducedOffersV2 = features[fixReducedOffersV2];
555
556 // With reducedOffersV2, it is not allowed to accept less than
557 // USD(0.5) of bob's USD offer. If we provide 1 drop for less
558 // than USD(0.5), then the remaining fractional offer would
559 // block the order book.
560 TER const expectedTER = reducedOffersV2 ? TER(tecPATH_DRY) : TER(tesSUCCESS);
561 env(pay(alice, carol, eur(1)),
562 Path(~XRP, ~eur),
563 Sendmax(usd(0.4)),
564 Txflags(tfNoRippleDirect | tfPartialPayment),
565 Ter(expectedTER));
566
567 if (!reducedOffersV2)
568 {
569 env.require(Balance(carol, eur(1)));
570 env.require(Balance(bob, usd(0.4)));
571 env.require(Balance(bob, eur(999)));
572
573 // Show that bob's USD offer is now a blocker.
574 SLE::const_pointer const usdOffer = env.le(bobUsdOffer);
575 if (BEAST_EXPECT(usdOffer))
576 {
577 std::uint64_t const bookRate = [&usdOffer]() {
578 // Extract the least significant 64 bits from the
579 // book page. That's where the quality is stored.
580 std::string bookDirStr = to_string(usdOffer->at(sfBookDirectory));
581 bookDirStr.erase(0, 48);
582 return std::stoull(bookDirStr, nullptr, 16);
583 }();
584 std::uint64_t const actualRate =
585 getRate(usdOffer->at(sfTakerGets), usdOffer->at(sfTakerPays));
586
587 // We expect the actual rate of the offer to be worse
588 // (larger) than the rate of the book page holding the
589 // offer. This is a defect which is corrected by
590 // fixReducedOffersV2.
591 BEAST_EXPECT(actualRate > bookRate);
592 }
593 }
594 }
595 }
596
597 void
599 {
600 testcase("Transfer Rate");
601
602 using namespace jtx;
603
604 auto const gw = Account("gateway");
605 auto const usd = gw["USD"];
606 auto const btc = gw["BTC"];
607 auto const eur = gw["EUR"];
608 Account const alice("alice");
609 Account const bob("bob");
610 Account const carol("carol");
611
612 // Offer where the owner is also the issuer, sender pays fee
613 Env env(*this, features);
614
615 env.fund(XRP(10000), alice, bob, gw);
616 env.close();
617 env(rate(gw, 1.25));
618 env.trust(usd(1000), alice, bob);
619 env(offer(gw, XRP(125), usd(125)));
620 env(pay(alice, bob, usd(100)), Sendmax(XRP(200)));
621 env.require(Balance(alice, xrpMinusFee(env, 10000 - 125)), Balance(bob, usd(100)));
622 }
623
624 void
626 {
627 testcase("falseDryChanges");
628
629 using namespace jtx;
630
631 auto const gw = Account("gateway");
632 auto const usd = gw["USD"];
633 auto const eur = gw["EUR"];
634 Account const alice("alice");
635 Account const bob("bob");
636 Account const carol("carol");
637
638 Env env(*this, features);
639
640 env.fund(XRP(10000), alice, carol, gw);
641 env.fund(reserve(env, 5), bob);
642 env.close();
643 env.trust(usd(1000), alice, bob, carol);
644 env.trust(eur(1000), alice, bob, carol);
645
646 env(pay(gw, alice, eur(50)));
647 env(pay(gw, bob, usd(50)));
648
649 // Bob has _just_ slightly less than 50 xrp available
650 // If his owner count changes, he will have more liquidity.
651 // This is one error case to test (when Flow is used).
652 // Computing the incoming xrp to the XRP/USD offer will require two
653 // recursive calls to the EUR/XRP offer. The second call will return
654 // tecPATH_DRY, but the entire path should not be marked as dry. This
655 // is the second error case to test (when flowV1 is used).
656 env(offer(bob, eur(50), XRP(50)));
657 env(offer(bob, XRP(50), usd(50)));
658
659 env(pay(alice, carol, usd(1000000)),
660 Path(~XRP, ~usd),
661 Sendmax(eur(500)),
662 Txflags(tfNoRippleDirect | tfPartialPayment));
663
664 auto const carolUSD = env.balance(carol, usd).value();
665 BEAST_EXPECT(carolUSD > usd(0) && carolUSD < usd(50));
666 }
667
668 void
670 {
671 // Single path with two offers and limit quality. The quality limit is
672 // such that the first offer should be taken but the second should not.
673 // The total amount delivered should be the sum of the two offers and
674 // sendMax should be more than the first offer.
675 testcase("limitQuality");
676 using namespace jtx;
677
678 auto const gw = Account("gateway");
679 auto const usd = gw["USD"];
680 Account const alice("alice");
681 Account const bob("bob");
682 Account const carol("carol");
683
684 {
685 Env env(*this);
686
687 env.fund(XRP(10000), alice, bob, carol, gw);
688 env.close();
689
690 env.trust(usd(100), alice, bob, carol);
691 env(pay(gw, bob, usd(100)));
692 env(offer(bob, XRP(50), usd(50)));
693 env(offer(bob, XRP(100), usd(50)));
694
695 env(pay(alice, carol, usd(100)),
696 Path(~usd),
697 Sendmax(XRP(100)),
698 Txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
699
700 env.require(Balance(carol, usd(50)));
701 }
702 }
703
704 // Helper function that returns the reserve on an account based on
705 // the passed in number of owners.
706 static XRPAmount
708 {
709 return env.current()->fees().accountReserve(count);
710 }
711
712 // Helper function that returns the Offers on an account.
715 {
717 forEachItem(*env.current(), account, [&result](SLE::const_ref sle) {
718 if (sle->getType() == ltOFFER)
719 result.push_back(sle);
720 });
721 return result;
722 }
723
724 void
726 {
727 testcase("Self-payment 1");
728
729 // In this test case the new flow code mis-computes the amount
730 // of money to move. Fortunately the new code's re-execute
731 // check catches the problem and throws out the transaction.
732 //
733 // The old payment code handles the payment correctly.
734 using namespace jtx;
735
736 auto const gw1 = Account("gw1");
737 auto const gw2 = Account("gw2");
738 auto const alice = Account("alice");
739 auto const usd = gw1["USD"];
740 auto const eur = gw2["EUR"];
741
742 Env env(*this, features);
743
744 env.fund(XRP(1000000), gw1, gw2);
745 env.close();
746
747 // The fee that's charged for transactions.
748 auto const f = env.current()->fees().base;
749
750 env.fund(reserve(env, 3) + f * 4, alice);
751 env.close();
752
753 env(trust(alice, usd(2000)));
754 env(trust(alice, eur(2000)));
755 env.close();
756
757 env(pay(gw1, alice, usd(1)));
758 env(pay(gw2, alice, eur(1000)));
759 env.close();
760
761 env(offer(alice, usd(500), eur(600)));
762 env.close();
763
764 env.require(Owners(alice, 3));
765 env.require(Balance(alice, usd(1)));
766 env.require(Balance(alice, eur(1000)));
767
768 auto aliceOffers = offersOnAccount(env, alice);
769 BEAST_EXPECT(aliceOffers.size() == 1);
770 for (auto const& offerPtr : aliceOffers)
771 {
772 auto const offer = *offerPtr;
773 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
774 BEAST_EXPECT(offer[sfTakerGets] == eur(600));
775 BEAST_EXPECT(offer[sfTakerPays] == usd(500));
776 }
777
778 env(pay(alice, alice, eur(600)), Sendmax(usd(500)), Txflags(tfPartialPayment));
779 env.close();
780
781 env.require(Owners(alice, 3));
782 env.require(Balance(alice, usd(1)));
783 env.require(Balance(alice, eur(1000)));
784 aliceOffers = offersOnAccount(env, alice);
785 BEAST_EXPECT(aliceOffers.size() == 1);
786 for (auto const& offerPtr : aliceOffers)
787 {
788 auto const offer = *offerPtr;
789 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
790 BEAST_EXPECT(offer[sfTakerGets] == eur(598.8));
791 BEAST_EXPECT(offer[sfTakerPays] == usd(499));
792 }
793 }
794
795 void
797 {
798 testcase("Self-payment 2");
799
800 // In this case the difference between the old payment code and
801 // the new is the values left behind in the offer. Not saying either
802 // ios ring, they are just different.
803 using namespace jtx;
804
805 auto const gw1 = Account("gw1");
806 auto const gw2 = Account("gw2");
807 auto const alice = Account("alice");
808 auto const usd = gw1["USD"];
809 auto const eur = gw2["EUR"];
810
811 Env env(*this, features);
812
813 env.fund(XRP(1000000), gw1, gw2);
814 env.close();
815
816 // The fee that's charged for transactions.
817 auto const f = env.current()->fees().base;
818
819 env.fund(reserve(env, 3) + f * 4, alice);
820 env.close();
821
822 env(trust(alice, usd(506)));
823 env(trust(alice, eur(606)));
824 env.close();
825
826 env(pay(gw1, alice, usd(500)));
827 env(pay(gw2, alice, eur(600)));
828 env.close();
829
830 env(offer(alice, usd(500), eur(600)));
831 env.close();
832
833 env.require(Owners(alice, 3));
834 env.require(Balance(alice, usd(500)));
835 env.require(Balance(alice, eur(600)));
836
837 auto aliceOffers = offersOnAccount(env, alice);
838 BEAST_EXPECT(aliceOffers.size() == 1);
839 for (auto const& offerPtr : aliceOffers)
840 {
841 auto const offer = *offerPtr;
842 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
843 BEAST_EXPECT(offer[sfTakerGets] == eur(600));
844 BEAST_EXPECT(offer[sfTakerPays] == usd(500));
845 }
846
847 env(pay(alice, alice, eur(60)), Sendmax(usd(50)), Txflags(tfPartialPayment));
848 env.close();
849
850 env.require(Owners(alice, 3));
851 env.require(Balance(alice, usd(500)));
852 env.require(Balance(alice, eur(600)));
853 aliceOffers = offersOnAccount(env, alice);
854 BEAST_EXPECT(aliceOffers.size() == 1);
855 for (auto const& offerPtr : aliceOffers)
856 {
857 auto const offer = *offerPtr;
858 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
859 BEAST_EXPECT(offer[sfTakerGets] == eur(594));
860 BEAST_EXPECT(offer[sfTakerPays] == usd(495));
861 }
862 }
863 void
864 testSelfFundedXRPEndpoint(bool consumeOffer, FeatureBitset features)
865 {
866 // Test that the deferred credit table is not bypassed for
867 // XRPEndpointSteps. If the account in the first step is sending XRP and
868 // that account also owns an offer that receives XRP, it should not be
869 // possible for that step to use the XRP received in the offer as part
870 // of the payment.
871 testcase("Self funded XRPEndpoint");
872
873 using namespace jtx;
874
875 Env env(*this, features);
876
877 auto const alice = Account("alice");
878 auto const gw = Account("gw");
879 auto const usd = gw["USD"];
880
881 env.fund(XRP(10000), alice, gw);
882 env.close();
883 env(trust(alice, usd(20)));
884 env(pay(gw, alice, usd(10)));
885 env(offer(alice, XRP(50000), usd(10)));
886
887 // Consuming the offer changes the owner count, which could also cause
888 // liquidity to decrease in the forward pass
889 auto const toSend = consumeOffer ? usd(10) : usd(9);
890 env(pay(alice, alice, toSend),
891 Path(~usd),
892 Sendmax(XRP(20000)),
893 Txflags(tfPartialPayment | tfNoRippleDirect));
894 }
895
896 void
898 {
899 testcase("Unfunded Offer");
900
901 using namespace jtx;
902 {
903 // Test reverse
904 Env env(*this, features);
905
906 auto const alice = Account("alice");
907 auto const bob = Account("bob");
908 auto const gw = Account("gw");
909 auto const usd = gw["USD"];
910
911 env.fund(XRP(100000), alice, bob, gw);
912 env.close();
913 env(trust(bob, usd(20)));
914
915 STAmount const tinyAmt1{usd, 9000000000000000ll, -17, false, STAmount::Unchecked{}};
916 STAmount const tinyAmt3{usd, 9000000000000003ll, -17, false, STAmount::Unchecked{}};
917
918 env(offer(gw, drops(9000000000), tinyAmt3));
919 env(pay(alice, bob, tinyAmt1),
920 Path(~usd),
921 Sendmax(drops(9000000000)),
922 Txflags(tfNoRippleDirect));
923
924 BEAST_EXPECT(!isOffer(env, gw, XRP(0), usd(0)));
925 }
926 {
927 // Test forward
928 Env env(*this, features);
929
930 auto const alice = Account("alice");
931 auto const bob = Account("bob");
932 auto const gw = Account("gw");
933 auto const usd = gw["USD"];
934
935 env.fund(XRP(100000), alice, bob, gw);
936 env.close();
937 env(trust(alice, usd(20)));
938
939 STAmount const tinyAmt1{usd, 9000000000000000ll, -17, false, STAmount::Unchecked{}};
940 STAmount const tinyAmt3{usd, 9000000000000003ll, -17, false, STAmount::Unchecked{}};
941
942 env(pay(gw, alice, tinyAmt1));
943
944 env(offer(gw, tinyAmt3, drops(9000000000)));
945 env(pay(alice, bob, drops(9000000000)),
946 Path(~XRP),
947 Sendmax(usd(1)),
948 Txflags(tfNoRippleDirect));
949
950 BEAST_EXPECT(!isOffer(env, gw, usd(0), XRP(0)));
951 }
952 }
953
954 void
956 {
957 testcase("ReExecuteDirectStep");
958
959 using namespace jtx;
960 Env env(*this, features);
961
962 auto const alice = Account("alice");
963 auto const bob = Account("bob");
964 auto const gw = Account("gw");
965 auto const usd = gw["USD"];
966 auto const usdC = usd.currency;
967
968 env.fund(XRP(10000), alice, bob, gw);
969 env.close();
970 env(trust(alice, usd(100)));
971 env.close();
972
973 BEAST_EXPECT(!getNoRippleFlag(env, gw, alice, usdC));
974
975 env(
976 pay(gw,
977 alice,
978 // 12.55....
979 STAmount{usd, std::uint64_t(1255555555555555ull), -14, false}));
980
981 env(offer(
982 gw,
983 // 5.0...
984 STAmount{usd, std::uint64_t(5000000000000000ull), -15, false},
985 XRP(1000)));
986
987 env(offer(
988 gw,
989 // .555...
990 STAmount{usd, std::uint64_t(5555555555555555ull), -16, false},
991 XRP(10)));
992
993 env(offer(
994 gw,
995 // 4.44....
996 STAmount{usd, std::uint64_t(4444444444444444ull), -15, false},
997 XRP(.1)));
998
999 env(offer(
1000 alice,
1001 // 17
1002 STAmount{usd, std::uint64_t(1700000000000000ull), -14, false},
1003 XRP(.001)));
1004
1005 env(pay(alice, bob, XRP(10000)),
1006 Path(~XRP),
1007 Sendmax(usd(100)),
1008 Txflags(tfPartialPayment | tfNoRippleDirect));
1009 }
1010
1011 void
1013 {
1014 testcase("ripd1443");
1015
1016 using namespace jtx;
1017 Env env(*this);
1018 auto const alice = Account("alice");
1019 auto const bob = Account("bob");
1020 auto const carol = Account("carol");
1021 auto const gw = Account("gw");
1022
1023 env.fund(XRP(100000000), alice, noripple(bob), carol, gw);
1024 env.close();
1025 env.trust(gw["USD"](10000), alice, carol);
1026 env(trust(bob, gw["USD"](10000), tfSetNoRipple));
1027 env.trust(gw["USD"](10000), bob);
1028 env.close();
1029
1030 // set no ripple between bob and the gateway
1031
1032 env(pay(gw, alice, gw["USD"](1000)));
1033 env.close();
1034
1035 env(offer(alice, bob["USD"](1000), XRP(1)));
1036 env.close();
1037
1038 env(pay(alice, alice, XRP(1)),
1039 Path(gw, bob, ~XRP),
1040 Sendmax(gw["USD"](1000)),
1041 Txflags(tfNoRippleDirect),
1042 Ter(tecPATH_DRY));
1043 env.close();
1044
1045 env.trust(bob["USD"](10000), alice);
1046 env(pay(bob, alice, bob["USD"](1000)));
1047
1048 env(offer(alice, XRP(1000), bob["USD"](1000)));
1049 env.close();
1050
1051 env(pay(carol, carol, gw["USD"](1000)),
1052 Path(~bob["USD"], gw),
1053 Sendmax(XRP(100000)),
1054 Txflags(tfNoRippleDirect),
1055 Ter(tecPATH_DRY));
1056 env.close();
1057
1058 pass();
1059 }
1060
1061 void
1063 {
1064 testcase("ripd1449");
1065
1066 using namespace jtx;
1067 Env env(*this);
1068
1069 // pay alice -> xrp -> USD/bob -> bob -> gw -> alice
1070 // set no ripple on bob's side of the bob/gw trust line
1071 // carol has the bob/USD and makes an offer, bob has USD/gw
1072
1073 auto const alice = Account("alice");
1074 auto const bob = Account("bob");
1075 auto const carol = Account("carol");
1076 auto const gw = Account("gw");
1077 auto const usd = gw["USD"];
1078
1079 env.fund(XRP(100000000), alice, bob, carol, gw);
1080 env.close();
1081 env.trust(usd(10000), alice, carol);
1082 env(trust(bob, usd(10000), tfSetNoRipple));
1083 env.trust(usd(10000), bob);
1084 env.trust(bob["USD"](10000), carol);
1085 env.close();
1086
1087 env(pay(bob, carol, bob["USD"](1000)));
1088 env(pay(gw, bob, usd(1000)));
1089 env.close();
1090
1091 env(offer(carol, XRP(1), bob["USD"](1000)));
1092 env.close();
1093
1094 env(pay(alice, alice, usd(1000)),
1095 Path(~bob["USD"], bob, gw),
1096 Sendmax(XRP(1)),
1097 Txflags(tfNoRippleDirect),
1098 Ter(tecPATH_DRY));
1099 env.close();
1100 }
1101
1102 void
1104 {
1105 // The new payment code used to assert if an offer was made for more
1106 // XRP than the offering account held. This unit test reproduces
1107 // that failing case.
1108 testcase("Self crossing low quality offer");
1109
1110 using namespace jtx;
1111
1112 Env env(*this, features);
1113
1114 auto const ann = Account("ann");
1115 auto const gw = Account("gateway");
1116 auto const ctb = gw["CTB"];
1117
1118 auto const fee = env.current()->fees().base;
1119 env.fund(reserve(env, 2) + drops(9999640) + fee, ann);
1120 env.fund(reserve(env, 2) + fee * 4, gw);
1121 env.close();
1122
1123 env(rate(gw, 1.002));
1124 env(trust(ann, ctb(10)));
1125 env.close();
1126
1127 env(pay(gw, ann, ctb(2.856)));
1128 env.close();
1129
1130 env(offer(ann, drops(365611702030), ctb(5.713)));
1131 env.close();
1132
1133 // This payment caused the assert.
1134 env(pay(ann, ann, ctb(0.687)), Sendmax(drops(20000000000)), Txflags(tfPartialPayment));
1135 }
1136
1137 void
1139 {
1140 testcase("Empty Strand");
1141 using namespace jtx;
1142
1143 auto const alice = Account("alice");
1144
1145 Env env(*this, features);
1146
1147 env.fund(XRP(10000), alice);
1148 env.close();
1149
1150 env(pay(alice, alice, alice["USD"](100)), Path(~alice["USD"]), Ter(temBAD_PATH));
1151 }
1152
1153 void
1155 {
1156 testcase("Circular XRP");
1157
1158 using namespace jtx;
1159 auto const alice = Account("alice");
1160 auto const bob = Account("bob");
1161 auto const gw = Account("gw");
1162 auto const usd = gw["USD"];
1163 auto const eur = gw["EUR"];
1164
1165 {
1166 // Payment path starting with XRP
1167 Env env(*this, testableAmendments());
1168 env.fund(XRP(10000), alice, bob, gw);
1169 env.close();
1170 env.trust(usd(1000), alice, bob);
1171 env.trust(eur(1000), alice, bob);
1172 env.close();
1173 env(pay(gw, alice, usd(100)));
1174 env(pay(gw, alice, eur(100)));
1175 env.close();
1176
1177 env(offer(alice, XRP(100), usd(100)), Txflags(tfPassive));
1178 env(offer(alice, usd(100), XRP(100)), Txflags(tfPassive));
1179 env(offer(alice, XRP(100), eur(100)), Txflags(tfPassive));
1180 env.close();
1181
1182 TER const expectedTer = TER{temBAD_PATH_LOOP};
1183 env(pay(alice, bob, eur(1)),
1184 Path(~usd, ~XRP, ~eur),
1185 Sendmax(XRP(1)),
1186 Txflags(tfNoRippleDirect),
1187 Ter(expectedTer));
1188
1189 pass();
1190 }
1191 {
1192 // Payment path ending with XRP
1193 Env env(*this);
1194 env.fund(XRP(10000), alice, bob, gw);
1195 env.close();
1196 env.trust(usd(1000), alice, bob);
1197 env.trust(eur(1000), alice, bob);
1198 env(pay(gw, alice, usd(100)));
1199 env(pay(gw, alice, eur(100)));
1200 env.close();
1201
1202 env(offer(alice, XRP(100), usd(100)), Txflags(tfPassive));
1203 env(offer(alice, eur(100), XRP(100)), Txflags(tfPassive));
1204 env.close();
1205 // EUR -> //XRP -> //USD ->XRP
1206 env(pay(alice, bob, XRP(1)),
1207 Path(~XRP, ~usd, ~XRP),
1208 Sendmax(eur(1)),
1209 Txflags(tfNoRippleDirect),
1211 }
1212 {
1213 // Payment where loop is formed in the middle of the path, not on an
1214 // endpoint
1215 auto const jpy = gw["JPY"];
1216 Env env(*this);
1217 env.fund(XRP(10000), alice, bob, gw);
1218 env.close();
1219 env.trust(usd(1000), alice, bob);
1220 env.trust(eur(1000), alice, bob);
1221 env.trust(jpy(1000), alice, bob);
1222 env.close();
1223 env(pay(gw, alice, usd(100)));
1224 env(pay(gw, alice, eur(100)));
1225 env(pay(gw, alice, jpy(100)));
1226 env.close();
1227
1228 env(offer(alice, usd(100), XRP(100)), Txflags(tfPassive));
1229 env(offer(alice, XRP(100), eur(100)), Txflags(tfPassive));
1230 env(offer(alice, eur(100), XRP(100)), Txflags(tfPassive));
1231 env(offer(alice, XRP(100), jpy(100)), Txflags(tfPassive));
1232 env.close();
1233
1234 env(pay(alice, bob, jpy(1)),
1235 Path(~XRP, ~eur, ~XRP, ~jpy),
1236 Sendmax(usd(1)),
1237 Txflags(tfNoRippleDirect),
1239 }
1240 }
1241
1242 void
1244 {
1245 testcase("Payment with ticket");
1246 using namespace jtx;
1247
1248 auto const alice = Account("alice");
1249 auto const bob = Account("bob");
1250
1251 Env env(*this, features);
1252
1253 env.fund(XRP(10000), alice);
1254 env.close();
1255
1256 // alice creates a ticket for the payment.
1257 std::uint32_t const ticketSeq{env.seq(alice) + 1};
1258 env(ticket::create(alice, 1));
1259
1260 // Make a payment using the ticket.
1261 env(pay(alice, bob, XRP(1000)), ticket::Use(ticketSeq));
1262 env.close();
1263 env.require(Balance(bob, XRP(1000)));
1264 env.require(Balance(alice, XRP(9000) - (env.current()->fees().base * 2)));
1265 }
1266
1267 void
1269 {
1270 using namespace jtx;
1271 FeatureBitset const reducedOffersV2(fixReducedOffersV2);
1272
1273 testLineQuality(features);
1274 testFalseDry(features);
1275 testBookStep(features - reducedOffersV2);
1276 testDirectStep(features);
1277 testBookStep(features);
1278 testTransferRate(features);
1279 testSelfPayment1(features);
1280 testSelfPayment2(features);
1281 testSelfFundedXRPEndpoint(false, features);
1282 testSelfFundedXRPEndpoint(true, features);
1283 testUnfundedOffer(features);
1284 testReExecuteDirectStep(features);
1286 testTicketPay(features);
1287 }
1288
1289 void
1290 run() override
1291 {
1294 testRIPD1443();
1295 testRIPD1449();
1296
1297 using namespace jtx;
1298 auto const sa = testableAmendments();
1299 testWithFeats(sa - featurePermissionedDEX);
1300 testWithFeats(sa);
1301 testEmptyStrand(sa);
1302 }
1303};
1304
1306{
1307 void
1308 run() override
1309 {
1310 using namespace jtx;
1311 auto const all = testableAmendments();
1312 FeatureBitset const permDex{featurePermissionedDEX};
1313
1314 testWithFeats(all - permDex);
1315 testWithFeats(all);
1316
1317 testEmptyStrand(all - permDex);
1318 testEmptyStrand(all);
1319 }
1320};
1321
1324
1325} // namespace xrpl::test
A generic endpoint for log messages.
Definition Journal.h:38
A testsuite class.
Definition suite.h:50
void pass()
Record a successful test condition.
Definition suite.h:500
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
A currency issued by an account.
Definition Issue.h:13
bool modify(modify_type const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
A wrapper which makes credits unavailable to balances.
std::shared_ptr< STLedgerEntry const > const & const_ref
std::shared_ptr< STLedgerEntry const > const_pointer
void pushBack(STPath const &e)
Definition STPathSet.h:540
Discardable, editable view to a ledger.
Definition Sandbox.h:15
void apply(RawView &to)
Definition Sandbox.h:35
virtual beast::Journal getJournal(std::string const &name)=0
virtual OpenLedger & getOpenLedger()=0
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
AccountID id() const
Returns the Account ID.
Definition jtx/Account.h:85
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
SLE::const_pointer le(Account const &account) const
Return an account root.
Definition Env.cpp:284
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:201
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:327
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:605
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
Match the number of items in the account's owner directory.
Definition owners.h:52
Add a path.
Definition paths.h:39
Set Paths, SendMax on a JTx.
Definition paths.h:16
Sets the QualityIn on a trust JTx.
Definition quality.h:24
Sets the QualityOut on a trust JTx as a percentage.
Definition quality.h:52
Sets the SendMax on a JTx.
Definition sendmax.h:13
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
Set the flags on a JTx.
Definition txflags.h:9
Set a ticket sequence on a JTx.
Definition ticket.h:26
T erase(T... args)
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition Indexes.cpp:264
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:241
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:16
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
PrettyAmount xrpMinusFee(Env const &env, std::int64_t xrpAmount)
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
FeatureBitset testableAmendments()
Definition Env.h:76
STPathElement ipe(Asset const &asset)
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
Definition Env.h:70
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:18
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:15
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(CrossingLimits, app, xrpl, 10)
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:48
BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, xrpl, 2)
bool isOffer(jtx::Env &env, jtx::Account const &account, STAmount const &takerPays, STAmount const &takerGets)
An offer exists.
Definition PathSet.h:48
bool getNoRippleFlag(jtx::Env const &env, jtx::Account const &src, jtx::Account const &dst, Currency const &cur)
Definition Flow_test.cpp:54
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:36
TER offerDelete(ApplyView &view, SLE::ref sle, beast::Journal j)
Delete an offer.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition StrandFlow.h:81
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:422
@ TapNone
Definition ApplyView.h:13
@ temBAD_PATH
Definition TER.h:82
@ temBAD_PATH_LOOP
Definition TER.h:83
TERSubset< CanCvtToTER > TER
Definition TER.h:634
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(SLE::const_ref)> const &f)
Iterate all items in the given directory.
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecPATH_PARTIAL
Definition TER.h:280
@ tecPATH_DRY
Definition TER.h:292
@ tesSUCCESS
Definition TER.h:240
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
T stoull(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
void run() override
Runs the suite.
void testLineQuality(FeatureBitset features)
void run() override
Runs the suite.
void testSelfPayLowQualityOffer(FeatureBitset features)
void testBookStep(FeatureBitset features)
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
void testSelfPayment1(FeatureBitset features)
void testUnfundedOffer(FeatureBitset features)
static std::vector< SLE::const_pointer > offersOnAccount(jtx::Env &env, jtx::Account account)
void testEmptyStrand(FeatureBitset features)
void testFalseDry(FeatureBitset features)
void testWithFeats(FeatureBitset features)
void testTicketPay(FeatureBitset features)
void testDirectStep(FeatureBitset features)
Definition Flow_test.cpp:72
void testTransferRate(FeatureBitset features)
void testSelfPayment2(FeatureBitset features)
void testSelfFundedXRPEndpoint(bool consumeOffer, FeatureBitset features)
void testReExecuteDirectStep(FeatureBitset features)
STAmount const & value() const