xrpld
Loading...
Searching...
No Matches
EscrowToken_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h> // IWYU pragma: keep
6#include <test/jtx/escrow.h>
7#include <test/jtx/fee.h>
8#include <test/jtx/flags.h>
9#include <test/jtx/mpt.h>
10#include <test/jtx/pay.h>
11#include <test/jtx/rate.h>
12#include <test/jtx/ter.h>
13#include <test/jtx/trust.h>
14#include <test/jtx/txflags.h>
15
16#include <xrpl/beast/unit_test/suite.h>
17#include <xrpl/beast/utility/Journal.h>
18#include <xrpl/core/ServiceRegistry.h>
19#include <xrpl/json/json_value.h>
20#include <xrpl/json/to_string.h>
21#include <xrpl/ledger/ApplyView.h>
22#include <xrpl/ledger/Dir.h>
23#include <xrpl/ledger/OpenView.h>
24#include <xrpl/ledger/Sandbox.h>
25#include <xrpl/protocol/AccountID.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/Issue.h>
29#include <xrpl/protocol/MPTIssue.h>
30#include <xrpl/protocol/Protocol.h>
31#include <xrpl/protocol/SField.h>
32#include <xrpl/protocol/STAmount.h>
33#include <xrpl/protocol/SeqProxy.h>
34#include <xrpl/protocol/TER.h>
35#include <xrpl/protocol/TxFlags.h>
36#include <xrpl/protocol/UintTypes.h>
37#include <xrpl/protocol/jss.h>
38
39#include <algorithm>
40#include <array>
41#include <cstdint>
42#include <iterator>
43#include <memory>
44
45namespace xrpl::test {
46
48{
49 static uint64_t
50 mptEscrowed(jtx::Env const& env, jtx::Account const& account, jtx::MPT const& mpt)
51 {
52 auto const sle = env.le(keylet::mptoken(mpt.mpt(), account));
53 if (sle && sle->isFieldPresent(sfLockedAmount))
54 return (*sle)[sfLockedAmount];
55 return 0;
56 }
57
58 static uint64_t
59 issuerMPTEscrowed(jtx::Env const& env, jtx::MPT const& mpt)
60 {
61 auto const sle = env.le(keylet::mptokenIssuance(mpt.mpt()));
62 if (sle && sle->isFieldPresent(sfLockedAmount))
63 return (*sle)[sfLockedAmount];
64 return 0;
65 }
66
68 issuerBalance(jtx::Env& env, jtx::Account const& account, Issue const& issue)
69 {
70 json::Value params;
71 params[jss::account] = account.human();
72 auto jrr = env.rpc("json", "gateway_balances", to_string(params));
73 auto const result = jrr[jss::result];
74 auto const obligations = result[jss::obligations][to_string(issue.currency)];
75 if (obligations.isNull())
76 return {STAmount(issue, 0), account.name()};
77 STAmount const amount = amountFromString(issue, obligations.asString());
78 return {amount, account.name()};
79 }
80
82 issuerEscrowed(jtx::Env& env, jtx::Account const& account, Issue const& issue)
83 {
84 json::Value params;
85 params[jss::account] = account.human();
86 auto jrr = env.rpc("json", "gateway_balances", to_string(params));
87 auto const result = jrr[jss::result];
88 auto const locked = result[jss::locked][to_string(issue.currency)];
89 if (locked.isNull())
90 return {STAmount(issue, 0), account.name()};
91 STAmount const amount = amountFromString(issue, locked.asString());
92 return {amount, account.name()};
93 }
94
95 void
97 {
98 testcase("IOU Enablement");
99
100 using namespace jtx;
101 using namespace std::chrono;
102
103 for (bool const withTokenEscrow : {false, true})
104 {
105 auto const amend = withTokenEscrow ? features : features - featureTokenEscrow;
106 Env env{*this, amend};
107 auto const baseFee = env.current()->fees().base;
108 auto const alice = Account("alice");
109 auto const bob = Account("bob");
110 auto const gw = Account{"gateway"};
111 auto const usd = gw["USD"];
112 env.fund(XRP(5000), alice, bob, gw);
113 env(fset(gw, asfAllowTrustLineLocking));
114 env.close();
115 env.trust(usd(10'000), alice, bob);
116 env.close();
117 env(pay(gw, alice, usd(5000)));
118 env(pay(gw, bob, usd(5000)));
119 env.close();
120
121 auto const createResult = withTokenEscrow ? Ter(tesSUCCESS) : Ter(temBAD_AMOUNT);
122 auto const finishResult = withTokenEscrow ? Ter(tesSUCCESS) : Ter(tecNO_TARGET);
123
124 auto const seq1 = env.seq(alice);
125 env(escrow::create(alice, bob, usd(1'000)),
127 escrow::kFinishTime(env.now() + 1s),
128 Fee(baseFee * 150),
129 createResult);
130 env.close();
131 env(escrow::finish(bob, alice, seq1),
134 Fee(baseFee * 150),
135 finishResult);
136 env.close();
137
138 auto const seq2 = env.seq(alice);
139 env(escrow::create(alice, bob, usd(1'000)),
141 escrow::kFinishTime(env.now() + 1s),
142 escrow::kCancelTime(env.now() + 2s),
143 Fee(baseFee * 150),
144 createResult);
145 env.close();
146 env(escrow::cancel(bob, alice, seq2), finishResult);
147 env.close();
148 }
149
150 for (bool const withTokenEscrow : {false, true})
151 {
152 auto const amend = withTokenEscrow ? features : features - featureTokenEscrow;
153 Env env{*this, amend};
154 auto const baseFee = env.current()->fees().base;
155 auto const alice = Account("alice");
156 auto const bob = Account("bob");
157 auto const gw = Account{"gateway"};
158 auto const usd = gw["USD"];
159 env.fund(XRP(5000), alice, bob, gw);
160 env(fset(gw, asfAllowTrustLineLocking));
161 env.close();
162 env.trust(usd(10'000), alice, bob);
163 env.close();
164 env(pay(gw, alice, usd(5000)));
165 env(pay(gw, bob, usd(5000)));
166 env.close();
167
168 auto const seq1 = env.seq(alice);
169 env(escrow::finish(bob, alice, seq1),
172 Fee(baseFee * 150),
174 env.close();
175
176 env(escrow::cancel(bob, alice, seq1), Ter(tecNO_TARGET));
177 env.close();
178 }
179 }
180
181 void
183 {
184 testcase("IOU Allow Locking Flag");
185
186 using namespace jtx;
187 using namespace std::chrono;
188
189 Env env{*this, features};
190 auto const baseFee = env.current()->fees().base;
191 auto const alice = Account("alice");
192 auto const bob = Account("bob");
193 auto const gw = Account{"gateway"};
194 auto const usd = gw["USD"];
195 env.fund(XRP(5000), alice, bob, gw);
196 env(fset(gw, asfAllowTrustLineLocking));
197 env.close();
198 env.trust(usd(10'000), alice, bob);
199 env.close();
200 env(pay(gw, alice, usd(5000)));
201 env(pay(gw, bob, usd(5000)));
202 env.close();
203
204 // Create Escrow #1 & #2
205 auto const seq1 = env.seq(alice);
206 env(escrow::create(alice, bob, usd(1'000)),
208 escrow::kFinishTime(env.now() + 1s),
209 Fee(baseFee * 150),
210 Ter(tesSUCCESS));
211 env.close();
212
213 auto const seq2 = env.seq(alice);
214 env(escrow::create(alice, bob, usd(1'000)),
215 escrow::kFinishTime(env.now() + 1s),
216 escrow::kCancelTime(env.now() + 3s),
217 Fee(baseFee),
218 Ter(tesSUCCESS));
219 env.close();
220
221 // Clear the asfAllowTrustLineLocking flag
222 env(fclear(gw, asfAllowTrustLineLocking));
223 env.close();
224 env.require(Nflags(gw, asfAllowTrustLineLocking));
225
226 // Cannot Create Escrow without asfAllowTrustLineLocking
227 env(escrow::create(alice, bob, usd(1'000)),
229 escrow::kFinishTime(env.now() + 1s),
230 Fee(baseFee * 150),
232 env.close();
233
234 // Can finish the escrow created before the flag was cleared
235 env(escrow::finish(bob, alice, seq1),
238 Fee(baseFee * 150),
239 Ter(tesSUCCESS));
240 env.close();
241
242 // Can cancel the escrow created before the flag was cleared
243 env(escrow::cancel(bob, alice, seq2), Ter(tesSUCCESS));
244 env.close();
245 }
246
247 void
249 {
250 testcase("IOU Create Preflight");
251 using namespace test::jtx;
252 using namespace std::literals;
253
254 // temBAD_FEE: Exercises invalid preflight1.
255 {
256 Env env{*this, features};
257 auto const alice = Account("alice");
258 auto const bob = Account("bob");
259 auto const gw = Account{"gateway"};
260 auto const usd = gw["USD"];
261 env.fund(XRP(5000), alice, bob, gw);
262
263 env(escrow::create(alice, bob, usd(1)),
264 escrow::kFinishTime(env.now() + 1s),
265 Fee(XRP(-1)),
266 Ter(temBAD_FEE));
267 env.close();
268 }
269
270 // temBAD_AMOUNT: amount <= 0
271 {
272 Env env{*this, features};
273 auto const baseFee = env.current()->fees().base;
274 auto const alice = Account("alice");
275 auto const bob = Account("bob");
276 auto const gw = Account{"gateway"};
277 auto const usd = gw["USD"];
278 env.fund(XRP(5000), alice, bob, gw);
279
280 env(escrow::create(alice, bob, usd(-1)),
282 escrow::kFinishTime(env.now() + 1s),
283 Fee(baseFee * 150),
285 env.close();
286 }
287
288 // temBAD_CURRENCY: badCurrency() == amount.getCurrency()
289 {
290 Env env{*this, features};
291 auto const baseFee = env.current()->fees().base;
292 auto const alice = Account("alice");
293 auto const bob = Account("bob");
294 auto const gw = Account{"gateway"};
295 auto const bad = IOU(gw, badCurrency());
296 env.fund(XRP(5000), alice, bob, gw);
297
298 env(escrow::create(alice, bob, bad(1)),
300 escrow::kFinishTime(env.now() + 1s),
301 Fee(baseFee * 150),
303 env.close();
304 }
305 }
306
307 void
309 {
310 testcase("IOU Create Preclaim");
311 using namespace test::jtx;
312 using namespace std::literals;
313
314 // tecNO_PERMISSION: issuer is the same as the account
315 {
316 Env env{*this, features};
317 auto const baseFee = env.current()->fees().base;
318 auto const alice = Account("alice");
319 auto const bob = Account("bob");
320 auto const gw = Account{"gateway"};
321 auto const usd = gw["USD"];
322 env.fund(XRP(5000), alice, bob, gw);
323
324 env(escrow::create(gw, alice, usd(1)),
326 escrow::kFinishTime(env.now() + 1s),
327 Fee(baseFee * 150),
329 env.close();
330 }
331
332 // tecNO_ISSUER: Issuer does not exist
333 {
334 Env env{*this, features};
335 auto const baseFee = env.current()->fees().base;
336 auto const alice = Account("alice");
337 auto const bob = Account("bob");
338 auto const gw = Account{"gateway"};
339 auto const usd = gw["USD"];
340 env.fund(XRP(5000), alice, bob);
341 env.close();
342 env.memoize(gw);
343
344 env(escrow::create(alice, bob, usd(1)),
346 escrow::kFinishTime(env.now() + 1s),
347 Fee(baseFee * 150),
349 env.close();
350 }
351
352 // tecNO_PERMISSION: asfAllowTrustLineLocking is not set
353 {
354 Env env{*this, features};
355 auto const baseFee = env.current()->fees().base;
356 auto const alice = Account("alice");
357 auto const bob = Account("bob");
358 auto const gw = Account{"gateway"};
359 auto const usd = gw["USD"];
360 env.fund(XRP(5000), alice, bob, gw);
361 env.close();
362 env.trust(usd(10'000), alice, bob);
363 env.close();
364 env(pay(gw, alice, usd(5000)));
365 env(pay(gw, bob, usd(5000)));
366 env.close();
367
368 env(escrow::create(gw, alice, usd(1)),
370 escrow::kFinishTime(env.now() + 1s),
371 Fee(baseFee * 150),
373 env.close();
374 }
375
376 // tecNO_LINE: account does not have a trustline to the issuer
377 {
378 Env env{*this, features};
379 auto const baseFee = env.current()->fees().base;
380 auto const alice = Account("alice");
381 auto const bob = Account("bob");
382 auto const gw = Account{"gateway"};
383 auto const usd = gw["USD"];
384 env.fund(XRP(5000), alice, bob, gw);
385 env(fset(gw, asfAllowTrustLineLocking));
386 env.close();
387 env(escrow::create(alice, bob, usd(1)),
389 escrow::kFinishTime(env.now() + 1s),
390 Fee(baseFee * 150),
391 Ter(tecNO_LINE));
392 env.close();
393 }
394
395 // tecNO_PERMISSION: Not testable
396 // tecNO_PERMISSION: Not testable
397 // tecNO_AUTH: requireAuth
398 {
399 Env env{*this, features};
400 auto const baseFee = env.current()->fees().base;
401 auto const alice = Account("alice");
402 auto const bob = Account("bob");
403 auto const gw = Account{"gateway"};
404 auto const usd = gw["USD"];
405 env.fund(XRP(5000), alice, bob, gw);
406 env(fset(gw, asfAllowTrustLineLocking));
407 env(fset(gw, asfRequireAuth));
408 env.close();
409 env.trust(usd(10'000), alice, bob);
410 env.close();
411
412 env(escrow::create(alice, bob, usd(1)),
414 escrow::kFinishTime(env.now() + 1s),
415 Fee(baseFee * 150),
416 Ter(tecNO_AUTH));
417 env.close();
418 }
419
420 // tecNO_AUTH: requireAuth
421 {
422 Env env{*this, features};
423 auto const baseFee = env.current()->fees().base;
424 auto const alice = Account("alice");
425 auto const bob = Account("bob");
426 auto const gw = Account{"gateway"};
427 auto const usd = gw["USD"];
428 auto const aliceUSD = alice["USD"];
429 env.fund(XRP(5000), alice, bob, gw);
430 env(fset(gw, asfAllowTrustLineLocking));
431 env(fset(gw, asfRequireAuth));
432 env.close();
433 env(trust(gw, aliceUSD(10'000)), Txflags(tfSetfAuth));
434 env.trust(usd(10'000), alice, bob);
435 env.close();
436
437 env(escrow::create(alice, bob, usd(1)),
439 escrow::kFinishTime(env.now() + 1s),
440 Fee(baseFee * 150),
441 Ter(tecNO_AUTH));
442 env.close();
443 }
444
445 // tecFROZEN: account is frozen
446 {
447 // Env Setup
448 Env env{*this, features};
449 auto const alice = Account("alice");
450 auto const bob = Account("bob");
451 auto const gw = Account{"gateway"};
452 auto const usd = gw["USD"];
453 auto const baseFee = env.current()->fees().base;
454 env.fund(XRP(10'000), alice, bob, gw);
455 env(fset(gw, asfAllowTrustLineLocking));
456 env.close();
457 env(trust(alice, usd(100'000)));
458 env(trust(bob, usd(100'000)));
459 env.close();
460 env(pay(gw, alice, usd(10'000)));
461 env(pay(gw, bob, usd(10'000)));
462 env.close();
463
464 // set freeze on alice trustline
465 env(trust(gw, usd(10'000), alice, tfSetFreeze));
466 env.close();
467
468 env(escrow::create(alice, bob, usd(1)),
470 escrow::kFinishTime(env.now() + 1s),
471 Fee(baseFee * 150),
472 Ter(tecFROZEN));
473 env.close();
474 }
475
476 // tecFROZEN: dest is frozen
477 {
478 // Env Setup
479 Env env{*this, features};
480 auto const alice = Account("alice");
481 auto const bob = Account("bob");
482 auto const gw = Account{"gateway"};
483 auto const usd = gw["USD"];
484 auto const baseFee = env.current()->fees().base;
485 env.fund(XRP(10'000), alice, bob, gw);
486 env(fset(gw, asfAllowTrustLineLocking));
487 env.close();
488 env(trust(alice, usd(100'000)));
489 env(trust(bob, usd(100'000)));
490 env.close();
491 env(pay(gw, alice, usd(10'000)));
492 env(pay(gw, bob, usd(10'000)));
493 env.close();
494
495 // set freeze on bob trustline
496 env(trust(gw, usd(10'000), bob, tfSetFreeze));
497 env.close();
498
499 env(escrow::create(alice, bob, usd(1)),
501 escrow::kFinishTime(env.now() + 1s),
502 Fee(baseFee * 150),
503 Ter(tecFROZEN));
504 env.close();
505 }
506
507 // tecINSUFFICIENT_FUNDS
508 {
509 // Env Setup
510 Env env{*this, features};
511 auto const alice = Account("alice");
512 auto const bob = Account("bob");
513 auto const gw = Account{"gateway"};
514 auto const usd = gw["USD"];
515 auto const baseFee = env.current()->fees().base;
516 env.fund(XRP(10'000), alice, bob, gw);
517 env(fset(gw, asfAllowTrustLineLocking));
518 env.close();
519 env(trust(alice, usd(100'000)));
520 env(trust(bob, usd(100'000)));
521 env.close();
522
523 env(escrow::create(alice, bob, usd(1)),
525 escrow::kFinishTime(env.now() + 1s),
526 Fee(baseFee * 150),
528 env.close();
529 }
530
531 // tecINSUFFICIENT_FUNDS
532 {
533 // Env Setup
534 Env env{*this, features};
535 auto const alice = Account("alice");
536 auto const bob = Account("bob");
537 auto const gw = Account{"gateway"};
538 auto const usd = gw["USD"];
539 auto const baseFee = env.current()->fees().base;
540 env.fund(XRP(10'000), alice, bob, gw);
541 env(fset(gw, asfAllowTrustLineLocking));
542 env.close();
543 env(trust(alice, usd(100'000)));
544 env(trust(bob, usd(100'000)));
545 env.close();
546 env(pay(gw, alice, usd(10'000)));
547 env(pay(gw, bob, usd(10'000)));
548 env.close();
549
550 env(escrow::create(alice, bob, usd(10'001)),
552 escrow::kFinishTime(env.now() + 1s),
553 Fee(baseFee * 150),
555 env.close();
556 }
557
558 // tecPRECISION_LOSS
559 {
560 Env env{*this, features};
561 auto const alice = Account("alice");
562 auto const bob = Account("bob");
563 auto const gw = Account{"gateway"};
564 auto const usd = gw["USD"];
565 auto const baseFee = env.current()->fees().base;
566 env.fund(XRP(10'000), alice, bob, gw);
567 env(fset(gw, asfAllowTrustLineLocking));
568 env.close();
569 env.trust(usd(100000000000000000), alice);
570 env.trust(usd(100000000000000000), bob);
571 env.close();
572 env(pay(gw, alice, usd(10000000000000000)));
573 env(pay(gw, bob, usd(1)));
574 env.close();
575
576 bool const largeMantissa = features[featureSingleAssetVault] ||
577 features[featureLendingProtocol] || features[featureMPTokensV2];
578
579 // alice cannot create escrow for 1/10 iou - precision loss
580 env(escrow::create(alice, bob, usd(1)),
582 escrow::kFinishTime(env.now() + 1s),
583 Fee(baseFee * 150),
584 Ter(largeMantissa ? (TER)tesSUCCESS : (TER)tecPRECISION_LOSS));
585 env.close();
586 }
587 }
588
589 void
591 {
592 testcase("IOU Finish Preclaim");
593 using namespace test::jtx;
594 using namespace std::literals;
595
596 // tecNO_AUTH: requireAuth set: dest not authorized
597 {
598 Env env{*this, features};
599 auto const baseFee = env.current()->fees().base;
600 auto const alice = Account("alice");
601 auto const bob = Account("bob");
602 auto const gw = Account{"gateway"};
603 auto const usd = gw["USD"];
604 auto const aliceUSD = alice["USD"];
605 auto const bobUSD = bob["USD"];
606 env.fund(XRP(5000), alice, bob, gw);
607 env(fset(gw, asfAllowTrustLineLocking));
608 env(fset(gw, asfRequireAuth));
609 env.close();
610 env(trust(gw, aliceUSD(10'000)), Txflags(tfSetfAuth));
611 env(trust(gw, bobUSD(10'000)), Txflags(tfSetfAuth));
612 env.trust(usd(10'000), alice, bob);
613 env.close();
614 env(pay(gw, alice, usd(10'000)));
615 env(pay(gw, bob, usd(10'000)));
616 env.close();
617
618 auto const seq1 = env.seq(alice);
619 env(escrow::create(alice, bob, usd(1)),
621 escrow::kFinishTime(env.now() + 1s),
622 Fee(baseFee * 150),
623 Ter(tesSUCCESS));
624 env.close();
625
626 env(pay(bob, gw, usd(10'000)));
627 env(trust(gw, bobUSD(0)), Txflags(tfSetfAuth));
628 env(trust(bob, usd(0)));
629 env.close();
630
631 env.trust(usd(10'000), bob);
632 env.close();
633
634 // bob cannot finish because he is not authorized
635 env(escrow::finish(bob, alice, seq1),
638 Fee(baseFee * 150),
639 Ter(tecNO_AUTH));
640 env.close();
641 }
642
643 // tecFROZEN: issuer has deep frozen the dest
644 {
645 Env env{*this, features};
646 auto const baseFee = env.current()->fees().base;
647 auto const alice = Account("alice");
648 auto const bob = Account("bob");
649 auto const gw = Account{"gateway"};
650 auto const usd = gw["USD"];
651 env.fund(XRP(5000), alice, bob, gw);
652 env(fset(gw, asfAllowTrustLineLocking));
653 env.close();
654 env.trust(usd(10'000), alice, bob);
655 env.close();
656 env(pay(gw, alice, usd(10'000)));
657 env(pay(gw, bob, usd(10'000)));
658 env.close();
659
660 auto const seq1 = env.seq(alice);
661 env(escrow::create(alice, bob, usd(1)),
663 escrow::kFinishTime(env.now() + 1s),
664 Fee(baseFee * 150),
665 Ter(tesSUCCESS));
666 env.close();
667
668 // set freeze on bob trustline
669 env(trust(gw, usd(10'000), bob, tfSetFreeze | tfSetDeepFreeze));
670
671 // bob cannot finish because of deep freeze
672 env(escrow::finish(bob, alice, seq1),
675 Fee(baseFee * 150),
676 Ter(tecFROZEN));
677 env.close();
678 }
679 }
680
681 void
683 {
684 testcase("IOU Finish Do Apply");
685 using namespace test::jtx;
686 using namespace std::literals;
687
688 // tecNO_LINE_INSUF_RESERVE: insufficient reserve to create line
689 {
690 Env env{*this, features};
691 auto const baseFee = env.current()->fees().base;
692 auto const acctReserve = env.current()->fees().reserve;
693 auto const incReserve = env.current()->fees().increment;
694 auto const alice = Account("alice");
695 auto const bob = Account("bob");
696 auto const gw = Account{"gateway"};
697 auto const usd = gw["USD"];
698 env.fund(XRP(5000), alice, gw);
699 env.fund(acctReserve + (incReserve - 1), bob);
700 env.close();
701 env(fset(gw, asfAllowTrustLineLocking));
702 env.close();
703 env.trust(usd(10'000), alice);
704 env.close();
705 env(pay(gw, alice, usd(10'000)));
706 env.close();
707
708 auto const seq1 = env.seq(alice);
709 env(escrow::create(alice, bob, usd(1)),
711 escrow::kFinishTime(env.now() + 1s),
712 Fee(baseFee * 150),
713 Ter(tesSUCCESS));
714 env.close();
715
716 // bob cannot finish because insufficient reserve to create line
717 env(escrow::finish(bob, alice, seq1),
720 Fee(baseFee * 150),
722 env.close();
723 }
724
725 // tecNO_LINE: alice submits; finish IOU not created
726 {
727 Env env{*this, features};
728 auto const baseFee = env.current()->fees().base;
729 auto const alice = Account("alice");
730 auto const bob = Account("bob");
731 auto const gw = Account{"gateway"};
732 auto const usd = gw["USD"];
733 env.fund(XRP(5000), alice, bob, gw);
734 env.close();
735 env(fset(gw, asfAllowTrustLineLocking));
736 env.close();
737 env.trust(usd(10'000), alice);
738 env.close();
739 env(pay(gw, alice, usd(10'000)));
740 env.close();
741
742 auto const seq1 = env.seq(alice);
743 env(escrow::create(alice, bob, usd(1)),
745 escrow::kFinishTime(env.now() + 1s),
746 Fee(baseFee * 150),
747 Ter(tesSUCCESS));
748 env.close();
749
750 // alice cannot finish because bob does not have a trustline
751 env(escrow::finish(alice, alice, seq1),
754 Fee(baseFee * 150),
755 Ter(tecNO_LINE));
756 env.close();
757 }
758
759 // tecLIMIT_EXCEEDED: alice submits; IOU Limit < balance + amount
760 {
761 Env env{*this, features};
762 auto const baseFee = env.current()->fees().base;
763 auto const alice = Account("alice");
764 auto const bob = Account("bob");
765 auto const gw = Account{"gateway"};
766 auto const usd = gw["USD"];
767 env.fund(XRP(5000), alice, bob, gw);
768 env.close();
769 env(fset(gw, asfAllowTrustLineLocking));
770 env.close();
771 env.trust(usd(1000), alice, bob);
772 env.close();
773 env(pay(gw, alice, usd(1000)));
774 env.close();
775
776 auto const seq1 = env.seq(alice);
777 env(escrow::create(alice, bob, usd(5)),
779 escrow::kFinishTime(env.now() + 1s),
780 Fee(baseFee * 150),
781 Ter(tesSUCCESS));
782 env.close();
783
784 env.trust(usd(1), bob);
785 env.close();
786
787 // alice cannot finish because bob's limit is too low
788 env(escrow::finish(alice, alice, seq1),
791 Fee(baseFee * 150),
793 env.close();
794 }
795
796 // tesSUCCESS: bob submits; IOU Limit < balance + amount
797 {
798 Env env{*this, features};
799 auto const baseFee = env.current()->fees().base;
800 auto const alice = Account("alice");
801 auto const bob = Account("bob");
802 auto const gw = Account{"gateway"};
803 auto const usd = gw["USD"];
804 env.fund(XRP(5000), alice, bob, gw);
805 env.close();
806 env(fset(gw, asfAllowTrustLineLocking));
807 env.close();
808 env.trust(usd(1000), alice, bob);
809 env.close();
810 env(pay(gw, alice, usd(1000)));
811 env.close();
812
813 auto const seq1 = env.seq(alice);
814 env(escrow::create(alice, bob, usd(5)),
816 escrow::kFinishTime(env.now() + 1s),
817 Fee(baseFee * 150),
818 Ter(tesSUCCESS));
819 env.close();
820
821 env.trust(usd(1), bob);
822 env.close();
823
824 // bob can finish even if bob's limit is too low
825 auto const bobPreLimit = env.limit(bob, usd);
826
827 env(escrow::finish(bob, alice, seq1),
830 Fee(baseFee * 150),
831 Ter(tesSUCCESS));
832 env.close();
833
834 // bob's limit is not changed
835 BEAST_EXPECT(env.limit(bob, usd) == bobPreLimit);
836 }
837 }
838
839 void
841 {
842 testcase("IOU Cancel Preclaim");
843 using namespace test::jtx;
844 using namespace std::literals;
845
846 // tecNO_AUTH: requireAuth set: account not authorized
847 {
848 Env env{*this, features};
849 auto const baseFee = env.current()->fees().base;
850 auto const alice = Account("alice");
851 auto const bob = Account("bob");
852 auto const gw = Account{"gateway"};
853 auto const usd = gw["USD"];
854 auto const aliceUSD = alice["USD"];
855 auto const bobUSD = bob["USD"];
856 env.fund(XRP(5000), alice, bob, gw);
857 env(fset(gw, asfAllowTrustLineLocking));
858 env(fset(gw, asfRequireAuth));
859 env.close();
860 env(trust(gw, aliceUSD(10'000)), Txflags(tfSetfAuth));
861 env(trust(gw, bobUSD(10'000)), Txflags(tfSetfAuth));
862 env.trust(usd(10'000), alice, bob);
863 env.close();
864 env(pay(gw, alice, usd(10'000)));
865 env(pay(gw, bob, usd(10'000)));
866 env.close();
867
868 auto const seq1 = env.seq(alice);
869 env(escrow::create(alice, bob, usd(1)),
870 escrow::kFinishTime(env.now() + 1s),
871 escrow::kCancelTime(env.now() + 2s),
872 Fee(baseFee),
873 Ter(tesSUCCESS));
874 env.close();
875
876 env(pay(alice, gw, usd(9'999)));
877 env(trust(gw, aliceUSD(0)), Txflags(tfSetfAuth));
878 env(trust(alice, usd(0)));
879 env.close();
880
881 env.trust(usd(10'000), alice);
882 env.close();
883
884 // alice cannot cancel because she is not authorized
885 env(escrow::cancel(bob, alice, seq1), Fee(baseFee), Ter(tecNO_AUTH));
886 env.close();
887 }
888 }
889
890 void
892 {
893 testcase("IOU Cancel DoApply");
894 using namespace jtx;
895 using namespace std::literals;
896
897 {
898 Env env{*this, features};
899 auto const baseFee = env.current()->fees().base;
900 auto const alice = Account("alice");
901 auto const bob = Account("bob");
902 auto const gw = Account("gw");
903 auto const usd = gw["USD"];
904
905 env.fund(XRP(10'000), alice, bob, gw);
906 env.close();
907
908 env(fset(gw, asfAllowTrustLineLocking));
909 env.close();
910
911 env.trust(usd(100'000), alice);
912 env.trust(usd(100'000), bob);
913 env.close();
914
915 env(pay(gw, alice, usd(10'000)));
916 env.close();
917
918 auto const seq = env.seq(alice);
919 env(escrow::create(alice, bob, usd(1'000)),
920 escrow::kFinishTime(env.now() + 1s),
921 escrow::kCancelTime(env.now() + 2s),
922 Fee(baseFee));
923 env.close();
924
925 BEAST_EXPECT(env.balance(alice, usd) == usd(9'000));
926
927 env(pay(alice, gw, usd(9'000)));
928 env.close();
929
930 env(trust(alice, usd(0)));
931 env.close();
932
933 auto const trustLineKey = keylet::trustLine(alice.id(), gw.id(), usd.currency);
934 BEAST_EXPECT(!env.current()->exists(trustLineKey));
935
936 env.close();
937 env.close();
938
939 auto const expectedResult = env.current()->rules().enabled(fixCleanup3_2_0)
940 ? Ter(tesSUCCESS)
941 : Ter(tefINTERNAL);
942 env(escrow::cancel(alice, alice, seq), Fee(baseFee), expectedResult);
943 env.close();
944
945 if (env.current()->rules().enabled(fixCleanup3_2_0))
946 {
947 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
948 BEAST_EXPECT(env.current()->exists(trustLineKey));
949 BEAST_EXPECT(env.balance(alice, usd) == usd(1'000));
950 }
951 }
952 }
953
954 void
956 {
957 testcase("IOU Cancel Reserve Recycle");
958 using namespace jtx;
959 using namespace std::literals;
960
961 // Escrowing the whole IOU balance lets the owner delete the now-zero
962 // trust line, so cancelling has to re-create it: one object destroyed,
963 // one created, and the reserve requirement unchanged.
964 Env env{*this, features};
965 bool const fixEnabled = env.current()->rules().enabled(fixCleanup3_4_0);
966
967 auto const baseFee = env.current()->fees().base;
968 auto const alice = Account("alice");
969 auto const bob = Account("bob");
970 auto const gw = Account("gw");
971 auto const usd = gw["USD"];
972
973 env.fund(XRP(10'000), alice, bob, gw);
974 env.close();
975
976 env(fset(gw, asfAllowTrustLineLocking));
977 env.close();
978
979 env.trust(usd(10'000), alice);
980 env.close();
981
982 env(pay(gw, alice, usd(10'000)));
983 env.close();
984 BEAST_EXPECT(env.ownerCount(alice) == 1);
985
986 auto const cancelAfter = env.now() + 100s;
987 auto const seq = env.seq(alice);
988 env(escrow::create(alice, bob, usd(10'000)),
989 escrow::kFinishTime(env.now() + 1s),
990 escrow::kCancelTime(cancelAfter),
991 Fee(baseFee));
992 env.close();
993 BEAST_EXPECT(env.ownerCount(alice) == 2);
994
995 auto const trustLineKey = keylet::trustLine(alice.id(), gw.id(), usd.currency);
996 env(trust(alice, usd(0)));
997 env.close();
998 BEAST_EXPECT(!env.current()->exists(trustLineKey));
999 BEAST_EXPECT(env.ownerCount(alice) == 1);
1000
1001 // Leave alice holding the reserve for exactly one owned object. That
1002 // is the escrow now and the re-created trust line after the cancel.
1003 auto const oneObject = env.current()->fees().accountReserve(1, 1);
1004 auto const twoObjects = env.current()->fees().accountReserve(2, 1);
1005 auto const balance = env.balance(alice).value().xrp();
1006 auto const feeCushion = baseFee.drops() * 20;
1007 env(pay(alice, bob, drops(balance.drops() - oneObject.drops() - feeCushion)));
1008 env.close();
1009 BEAST_EXPECT(env.balance(alice).value().xrp() >= oneObject);
1010 BEAST_EXPECT(env.balance(alice).value().xrp() < twoObjects);
1011
1012 for (; env.now() < cancelAfter; env.close())
1013 {
1014 }
1015 env.close();
1016 env.close();
1017
1018 auto const expectedResult = fixEnabled ? Ter(tesSUCCESS) : Ter(tecNO_LINE_INSUF_RESERVE);
1019 env(escrow::cancel(alice, alice, seq), Fee(baseFee), expectedResult);
1020 env.close();
1021
1022 auto const escrowKey = keylet::escrow(alice.id(), SeqProxy::rawSequence(seq));
1023 if (fixEnabled)
1024 {
1025 BEAST_EXPECT(!env.le(escrowKey));
1026 BEAST_EXPECT(env.current()->exists(trustLineKey));
1027 BEAST_EXPECT(env.balance(alice, usd) == usd(10'000));
1028 BEAST_EXPECT(env.ownerCount(alice) == 1);
1029 }
1030 else
1031 {
1032 // The tec keeps the escrow, so one more owner reserve lets the
1033 // retry through.
1034 BEAST_EXPECT(env.le(escrowKey) != nullptr);
1035 BEAST_EXPECT(!env.current()->exists(trustLineKey));
1036 BEAST_EXPECT(env.ownerCount(alice) == 1);
1037
1038 env(pay(bob, alice, drops(twoObjects.drops() - oneObject.drops())));
1039 env.close();
1040 env(escrow::cancel(alice, alice, seq), Fee(baseFee), Ter(tesSUCCESS));
1041 env.close();
1042 BEAST_EXPECT(!env.le(escrowKey));
1043 BEAST_EXPECT(env.balance(alice, usd) == usd(10'000));
1044 }
1045 }
1046
1047 void
1049 {
1050 testcase("IOU Balances");
1051
1052 using namespace jtx;
1053 using namespace std::chrono;
1054
1055 Env env{*this, features};
1056 auto const baseFee = env.current()->fees().base;
1057 auto const alice = Account("alice");
1058 auto const bob = Account("bob");
1059 auto const gw = Account{"gateway"};
1060 auto const usd = gw["USD"];
1061 env.fund(XRP(5000), alice, bob, gw);
1062 env(fset(gw, asfAllowTrustLineLocking));
1063 env.close();
1064 env.trust(usd(10'000), alice, bob);
1065 env.close();
1066 env(pay(gw, alice, usd(5'000)));
1067 env(pay(gw, bob, usd(5'000)));
1068 env.close();
1069
1070 auto const outstandingUSD = usd(10'000);
1071
1072 // Create & Finish Escrow
1073 auto const seq1 = env.seq(alice);
1074 {
1075 auto const preAliceUSD = env.balance(alice, usd);
1076 auto const preBobUSD = env.balance(bob, usd);
1077 env(escrow::create(alice, bob, usd(1'000)),
1079 escrow::kFinishTime(env.now() + 1s),
1080 Fee(baseFee * 150),
1081 Ter(tesSUCCESS));
1082 env.close();
1083
1084 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD - usd(1'000));
1085 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD);
1086 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD - usd(1'000));
1087 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(1'000));
1088 }
1089 {
1090 auto const preAliceUSD = env.balance(alice, usd);
1091 auto const preBobUSD = env.balance(bob, usd);
1092 env(escrow::finish(bob, alice, seq1),
1095 Fee(baseFee * 150),
1096 Ter(tesSUCCESS));
1097 env.close();
1098
1099 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD);
1100 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD + usd(1'000));
1101 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD);
1102 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(0));
1103 }
1104
1105 // Create & Cancel Escrow
1106 auto const seq2 = env.seq(alice);
1107 {
1108 auto const preAliceUSD = env.balance(alice, usd);
1109 auto const preBobUSD = env.balance(bob, usd);
1110 env(escrow::create(alice, bob, usd(1'000)),
1112 escrow::kFinishTime(env.now() + 1s),
1113 escrow::kCancelTime(env.now() + 2s),
1114 Fee(baseFee * 150),
1115 Ter(tesSUCCESS));
1116 env.close();
1117
1118 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD - usd(1'000));
1119 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD);
1120 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD - usd(1'000));
1121 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(1'000));
1122 }
1123 {
1124 auto const preAliceUSD = env.balance(alice, usd);
1125 auto const preBobUSD = env.balance(bob, usd);
1126 env(escrow::cancel(bob, alice, seq2), Ter(tesSUCCESS));
1127 env.close();
1128
1129 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD + usd(1'000));
1130 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD);
1131 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD);
1132 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(0));
1133 }
1134 }
1135
1136 void
1138 {
1139 using namespace jtx;
1140 using namespace std::chrono;
1141
1142 auto const alice = Account("alice");
1143 auto const bob = Account("bob");
1144 auto const carol = Account("carol");
1145 auto const gw = Account{"gateway"};
1146 auto const usd = gw["USD"];
1147 {
1148 testcase("IOU Metadata to self");
1149
1150 Env env{*this, features};
1151 env.fund(XRP(5000), alice, bob, carol, gw);
1152 env(fset(gw, asfAllowTrustLineLocking));
1153 env.close();
1154 env.trust(usd(10'000), alice, bob, carol);
1155 env.close();
1156 env(pay(gw, alice, usd(5000)));
1157 env(pay(gw, bob, usd(5000)));
1158 env(pay(gw, carol, usd(5000)));
1159 env.close();
1160 auto const aseq = env.seq(alice);
1161 auto const bseq = env.seq(bob);
1162
1163 env(escrow::create(alice, alice, usd(1'000)),
1164 escrow::kFinishTime(env.now() + 1s),
1165 escrow::kCancelTime(env.now() + 500s));
1166 BEAST_EXPECT(
1167 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1168 env.close(5s);
1169 auto const aa = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
1170 BEAST_EXPECT(aa);
1171 {
1172 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1173 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
1174 BEAST_EXPECT(
1175 // NOLINTNEXTLINE(modernize-use-ranges)
1176 std::find(aod.begin(), aod.end(), aa) != aod.end());
1177 }
1178
1179 {
1180 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1181 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4);
1182 BEAST_EXPECT(
1183 // NOLINTNEXTLINE(modernize-use-ranges)
1184 std::find(iod.begin(), iod.end(), aa) != iod.end());
1185 }
1186
1187 env(escrow::create(bob, bob, usd(1'000)),
1188 escrow::kFinishTime(env.now() + 1s),
1189 escrow::kCancelTime(env.now() + 2s));
1190 BEAST_EXPECT(
1191 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1192 env.close(5s);
1193 auto const bb = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
1194 BEAST_EXPECT(bb);
1195
1196 {
1197 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1198 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1199 BEAST_EXPECT(
1200 // NOLINTNEXTLINE(modernize-use-ranges)
1201 std::find(bod.begin(), bod.end(), bb) != bod.end());
1202 }
1203
1204 {
1205 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1206 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 5);
1207 BEAST_EXPECT(
1208 // NOLINTNEXTLINE(modernize-use-ranges)
1209 std::find(iod.begin(), iod.end(), bb) != iod.end());
1210 }
1211
1212 env.close(5s);
1213 env(escrow::finish(alice, alice, aseq));
1214 {
1215 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1216 BEAST_EXPECT(
1217 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1218
1219 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1220 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1221 BEAST_EXPECT(
1222 // NOLINTNEXTLINE(modernize-use-ranges)
1223 std::find(aod.begin(), aod.end(), aa) == aod.end());
1224
1225 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1226 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1227 BEAST_EXPECT(
1228 // NOLINTNEXTLINE(modernize-use-ranges)
1229 std::find(bod.begin(), bod.end(), bb) != bod.end());
1230
1231 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1232 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4);
1233 BEAST_EXPECT(
1234 // NOLINTNEXTLINE(modernize-use-ranges)
1235 std::find(iod.begin(), iod.end(), bb) != iod.end());
1236 }
1237
1238 env.close(5s);
1239 env(escrow::cancel(bob, bob, bseq));
1240 {
1241 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
1242 BEAST_EXPECT(
1243 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1244
1245 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1246 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1247 BEAST_EXPECT(
1248 // NOLINTNEXTLINE(modernize-use-ranges)
1249 std::find(bod.begin(), bod.end(), bb) == bod.end());
1250
1251 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1252 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3);
1253 BEAST_EXPECT(
1254 // NOLINTNEXTLINE(modernize-use-ranges)
1255 std::find(iod.begin(), iod.end(), bb) == iod.end());
1256 }
1257 }
1258 {
1259 testcase("IOU Metadata to other");
1260
1261 Env env{*this, features};
1262 env.fund(XRP(5000), alice, bob, carol, gw);
1263 env(fset(gw, asfAllowTrustLineLocking));
1264 env.close();
1265 env.trust(usd(10'000), alice, bob, carol);
1266 env.close();
1267 env(pay(gw, alice, usd(5000)));
1268 env(pay(gw, bob, usd(5000)));
1269 env(pay(gw, carol, usd(5000)));
1270 env.close();
1271 auto const aseq = env.seq(alice);
1272 auto const bseq = env.seq(bob);
1273
1274 env(escrow::create(alice, bob, usd(1'000)), escrow::kFinishTime(env.now() + 1s));
1275 BEAST_EXPECT(
1276 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1277 env.close(5s);
1278 env(escrow::create(bob, carol, usd(1'000)),
1279 escrow::kFinishTime(env.now() + 1s),
1280 escrow::kCancelTime(env.now() + 2s));
1281 BEAST_EXPECT(
1282 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1283 env.close(5s);
1284
1285 auto const ab = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
1286 BEAST_EXPECT(ab);
1287
1288 auto const bc = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
1289 BEAST_EXPECT(bc);
1290
1291 {
1292 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1293 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
1294 BEAST_EXPECT(
1295 // NOLINTNEXTLINE(modernize-use-ranges)
1296 std::find(aod.begin(), aod.end(), ab) != aod.end());
1297
1298 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1299 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 3);
1300 BEAST_EXPECT(
1301 // NOLINTNEXTLINE(modernize-use-ranges)
1302 std::find(bod.begin(), bod.end(), ab) != bod.end());
1303 BEAST_EXPECT(
1304 // NOLINTNEXTLINE(modernize-use-ranges)
1305 std::find(bod.begin(), bod.end(), bc) != bod.end());
1306
1307 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1308 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
1309 BEAST_EXPECT(
1310 // NOLINTNEXTLINE(modernize-use-ranges)
1311 std::find(cod.begin(), cod.end(), bc) != cod.end());
1312
1313 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1314 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 5);
1315 BEAST_EXPECT(
1316 // NOLINTNEXTLINE(modernize-use-ranges)
1317 std::find(iod.begin(), iod.end(), ab) != iod.end());
1318 BEAST_EXPECT(
1319 // NOLINTNEXTLINE(modernize-use-ranges)
1320 std::find(iod.begin(), iod.end(), bc) != iod.end());
1321 }
1322
1323 env.close(5s);
1324 env(escrow::finish(alice, alice, aseq));
1325 {
1326 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1327 BEAST_EXPECT(env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
1328
1329 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1330 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1331 BEAST_EXPECT(
1332 // NOLINTNEXTLINE(modernize-use-ranges)
1333 std::find(aod.begin(), aod.end(), ab) == aod.end());
1334
1335 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1336 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1337 BEAST_EXPECT(
1338 // NOLINTNEXTLINE(modernize-use-ranges)
1339 std::find(bod.begin(), bod.end(), ab) == bod.end());
1340 BEAST_EXPECT(
1341 // NOLINTNEXTLINE(modernize-use-ranges)
1342 std::find(bod.begin(), bod.end(), bc) != bod.end());
1343
1344 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1345 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
1346
1347 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1348 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4);
1349 BEAST_EXPECT(
1350 // NOLINTNEXTLINE(modernize-use-ranges)
1351 std::find(iod.begin(), iod.end(), ab) == iod.end());
1352 BEAST_EXPECT(
1353 // NOLINTNEXTLINE(modernize-use-ranges)
1354 std::find(iod.begin(), iod.end(), bc) != iod.end());
1355 }
1356
1357 env.close(5s);
1358 env(escrow::cancel(bob, bob, bseq));
1359 {
1360 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1361 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
1362
1363 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1364 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1365 BEAST_EXPECT(
1366 // NOLINTNEXTLINE(modernize-use-ranges)
1367 std::find(aod.begin(), aod.end(), ab) == aod.end());
1368
1369 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1370 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1371 BEAST_EXPECT(
1372 // NOLINTNEXTLINE(modernize-use-ranges)
1373 std::find(bod.begin(), bod.end(), ab) == bod.end());
1374 BEAST_EXPECT(
1375 // NOLINTNEXTLINE(modernize-use-ranges)
1376 std::find(bod.begin(), bod.end(), bc) == bod.end());
1377
1378 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1379 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1380
1381 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1382 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3);
1383 BEAST_EXPECT(
1384 // NOLINTNEXTLINE(modernize-use-ranges)
1385 std::find(iod.begin(), iod.end(), ab) == iod.end());
1386 BEAST_EXPECT(
1387 // NOLINTNEXTLINE(modernize-use-ranges)
1388 std::find(iod.begin(), iod.end(), bc) == iod.end());
1389 }
1390 }
1391
1392 {
1393 testcase("IOU Metadata to issuer");
1394
1395 Env env{*this, features};
1396 env.fund(XRP(5000), alice, carol, gw);
1397 env(fset(gw, asfAllowTrustLineLocking));
1398 env.close();
1399 env.trust(usd(10'000), alice, carol);
1400 env.close();
1401 env(pay(gw, alice, usd(5000)));
1402 env(pay(gw, carol, usd(5000)));
1403 env.close();
1404 auto const aseq = env.seq(alice);
1405
1406 env(escrow::create(alice, gw, usd(1'000)), escrow::kFinishTime(env.now() + 1s));
1407
1408 BEAST_EXPECT(
1409 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1410 env.close(5s);
1411 env(escrow::create(gw, carol, usd(1'000)),
1412 escrow::kFinishTime(env.now() + 1s),
1413 escrow::kCancelTime(env.now() + 2s),
1415 env.close(5s);
1416
1417 auto const ag = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
1418 BEAST_EXPECT(ag);
1419
1420 {
1421 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1422 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
1423 BEAST_EXPECT(
1424 // NOLINTNEXTLINE(modernize-use-ranges)
1425 std::find(aod.begin(), aod.end(), ag) != aod.end());
1426
1427 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1428 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1429
1430 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1431 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3);
1432 BEAST_EXPECT(
1433 // NOLINTNEXTLINE(modernize-use-ranges)
1434 std::find(iod.begin(), iod.end(), ag) != iod.end());
1435 }
1436
1437 env.close(5s);
1438 env(escrow::finish(alice, alice, aseq));
1439 {
1440 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1441
1442 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1443 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1444 BEAST_EXPECT(
1445 // NOLINTNEXTLINE(modernize-use-ranges)
1446 std::find(aod.begin(), aod.end(), ag) == aod.end());
1447
1448 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1449 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1450
1451 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1452 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 2);
1453 BEAST_EXPECT(
1454 // NOLINTNEXTLINE(modernize-use-ranges)
1455 std::find(iod.begin(), iod.end(), ag) == iod.end());
1456 }
1457 }
1458 }
1459
1460 void
1462 {
1463 testcase("IOU RippleState");
1464 using namespace test::jtx;
1465 using namespace std::literals;
1466
1467 struct TestAccountData
1468 {
1469 Account src;
1470 Account dst;
1471 Account gw;
1472 bool hasTrustline;
1473 bool negative;
1474 };
1475
1477 // src > dst && src > issuer && dst no trustline
1478 {.src = Account("alice2"),
1479 .dst = Account("bob0"),
1480 .gw = Account{"gw0"},
1481 .hasTrustline = false,
1482 .negative = true},
1483 // src < dst && src < issuer && dst no trustline
1484 {.src = Account("carol0"),
1485 .dst = Account("dan1"),
1486 .gw = Account{"gw1"},
1487 .hasTrustline = false,
1488 .negative = false},
1489 // dst > src && dst > issuer && dst no trustline
1490 {.src = Account("dan1"),
1491 .dst = Account("alice2"),
1492 .gw = Account{"gw0"},
1493 .hasTrustline = false,
1494 .negative = true},
1495 // dst < src && dst < issuer && dst no trustline
1496 {.src = Account("bob0"),
1497 .dst = Account("carol0"),
1498 .gw = Account{"gw1"},
1499 .hasTrustline = false,
1500 .negative = false},
1501 // src > dst && src > issuer && dst has trustline
1502 {.src = Account("alice2"),
1503 .dst = Account("bob0"),
1504 .gw = Account{"gw0"},
1505 .hasTrustline = true,
1506 .negative = true},
1507 // src < dst && src < issuer && dst has trustline
1508 {.src = Account("carol0"),
1509 .dst = Account("dan1"),
1510 .gw = Account{"gw1"},
1511 .hasTrustline = true,
1512 .negative = false},
1513 // dst > src && dst > issuer && dst has trustline
1514 {.src = Account("dan1"),
1515 .dst = Account("alice2"),
1516 .gw = Account{"gw0"},
1517 .hasTrustline = true,
1518 .negative = true},
1519 // dst < src && dst < issuer && dst has trustline
1520 {.src = Account("bob0"),
1521 .dst = Account("carol0"),
1522 .gw = Account{"gw1"},
1523 .hasTrustline = true,
1524 .negative = false},
1525 }};
1526
1527 for (auto const& t : tests)
1528 {
1529 Env env{*this, features};
1530 auto const baseFee = env.current()->fees().base;
1531 auto const usd = t.gw["USD"];
1532 env.fund(XRP(5000), t.src, t.dst, t.gw);
1533 env(fset(t.gw, asfAllowTrustLineLocking));
1534 env.close();
1535
1536 if (t.hasTrustline)
1537 {
1538 env.trust(usd(100'000), t.src, t.dst);
1539 }
1540 else
1541 {
1542 env.trust(usd(100'000), t.src);
1543 }
1544 env.close();
1545
1546 env(pay(t.gw, t.src, usd(10'000)));
1547 if (t.hasTrustline)
1548 env(pay(t.gw, t.dst, usd(10'000)));
1549 env.close();
1550
1551 // src can create escrow
1552 auto const seq1 = env.seq(t.src);
1553 auto const delta = usd(1'000);
1554 env(escrow::create(t.src, t.dst, delta),
1556 escrow::kFinishTime(env.now() + 1s),
1557 Fee(baseFee * 150));
1558 env.close();
1559
1560 // dst can finish escrow
1561 auto const preSrc = env.balance(t.src, usd);
1562 auto const preDst = env.balance(t.dst, usd);
1563
1564 env(escrow::finish(t.dst, t.src, seq1),
1567 Fee(baseFee * 150));
1568 env.close();
1569
1570 BEAST_EXPECT(env.balance(t.src, usd) == preSrc);
1571 BEAST_EXPECT(env.balance(t.dst, usd) == preDst + delta);
1572 }
1573 }
1574
1575 void
1577 {
1578 testcase("IOU Gateway");
1579 using namespace test::jtx;
1580 using namespace std::literals;
1581
1582 struct TestAccountData
1583 {
1584 Account src;
1585 Account dst;
1586 bool hasTrustline;
1587 };
1588
1589 // issuer is source
1590 {
1591 auto const gw = Account{"gateway"};
1592 auto const alice = Account{"alice"};
1593 Env env{*this, features};
1594 auto const baseFee = env.current()->fees().base;
1595 auto const usd = gw["USD"];
1596 env.fund(XRP(5000), alice, gw);
1597 env(fset(gw, asfAllowTrustLineLocking));
1598 env.close();
1599 env.trust(usd(100'000), alice);
1600 env.close();
1601
1602 env(pay(gw, alice, usd(10'000)));
1603 env.close();
1604
1605 // issuer cannot create escrow
1606 env(escrow::create(gw, alice, usd(1'000)),
1608 escrow::kFinishTime(env.now() + 1s),
1609 Fee(baseFee * 150),
1611 env.close();
1612 }
1613
1614 std::array<TestAccountData, 4> const gwDstTests = {{
1615 // src > dst && src > issuer && dst has trustline
1616 {.src = Account("alice2"), .dst = Account{"gw0"}, .hasTrustline = true},
1617 // src < dst && src < issuer && dst has trustline
1618 {.src = Account("carol0"), .dst = Account{"gw1"}, .hasTrustline = true},
1619 // dst > src && dst > issuer && dst has trustline
1620 {.src = Account("dan1"), .dst = Account{"gw0"}, .hasTrustline = true},
1621 // dst < src && dst < issuer && dst has trustline
1622 {.src = Account("bob0"), .dst = Account{"gw1"}, .hasTrustline = true},
1623 }};
1624
1625 // issuer is destination
1626 for (auto const& t : gwDstTests)
1627 {
1628 Env env{*this, features};
1629 auto const baseFee = env.current()->fees().base;
1630 auto const usd = t.dst["USD"];
1631 env.fund(XRP(5000), t.dst, t.src);
1632 env(fset(t.dst, asfAllowTrustLineLocking));
1633 env.close();
1634
1635 env.trust(usd(100'000), t.src);
1636 env.close();
1637
1638 env(pay(t.dst, t.src, usd(10'000)));
1639 env.close();
1640
1641 // issuer can receive escrow
1642 auto const seq1 = env.seq(t.src);
1643 auto const preSrc = env.balance(t.src, usd);
1644 env(escrow::create(t.src, t.dst, usd(1'000)),
1646 escrow::kFinishTime(env.now() + 1s),
1647 Fee(baseFee * 150));
1648 env.close();
1649
1650 // issuer can finish escrow, no dest trustline
1651 env(escrow::finish(t.dst, t.src, seq1),
1654 Fee(baseFee * 150));
1655 env.close();
1656 auto const preAmount = 10'000;
1657 BEAST_EXPECT(preSrc == usd(preAmount));
1658 auto const postAmount = 9000;
1659 BEAST_EXPECT(env.balance(t.src, usd) == usd(postAmount));
1660 BEAST_EXPECT(env.balance(t.dst, usd) == usd(0));
1661 }
1662
1663 // issuer is source and destination
1664 {
1665 auto const gw = Account{"gateway"};
1666 auto const usd = gw["USD"];
1667 Env env{*this, features};
1668 auto const baseFee = env.current()->fees().base;
1669 env.fund(XRP(5000), gw);
1670 env(fset(gw, asfAllowTrustLineLocking));
1671 env.close();
1672
1673 // issuer cannot receive escrow
1674 env(escrow::create(gw, gw, usd(1'000)),
1676 escrow::kFinishTime(env.now() + 1s),
1677 Fee(baseFee * 150),
1679 env.close();
1680 }
1681 }
1682
1683 void
1685 {
1686 testcase("IOU Locked Rate");
1687 using namespace test::jtx;
1688 using namespace std::literals;
1689
1690 auto const alice = Account("alice");
1691 auto const bob = Account("bob");
1692 auto const carol = Account("carol");
1693 auto const gw = Account{"gateway"};
1694 auto const usd = gw["USD"];
1695
1696 // test locked rate
1697 {
1698 Env env{*this, features};
1699 auto const baseFee = env.current()->fees().base;
1700 env.fund(XRP(10'000), alice, bob, gw);
1701 env(fset(gw, asfAllowTrustLineLocking));
1702 env(rate(gw, 1.25));
1703 env.close();
1704 env.trust(usd(100'000), alice);
1705 env.trust(usd(100'000), bob);
1706 env.close();
1707 env(pay(gw, alice, usd(10'000)));
1708 env(pay(gw, bob, usd(10'000)));
1709 env.close();
1710
1711 // alice can create escrow w/ xfer rate
1712 auto const preAlice = env.balance(alice, usd);
1713 auto const seq1 = env.seq(alice);
1714 auto const delta = usd(125);
1715 env(escrow::create(alice, bob, delta),
1717 escrow::kFinishTime(env.now() + 1s),
1718 Fee(baseFee * 150));
1719 env.close();
1720 auto const transferRate = escrow::rate(env, alice, seq1);
1721 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1722
1723 // bob can finish escrow
1724 env(escrow::finish(bob, alice, seq1),
1727 Fee(baseFee * 150));
1728 env.close();
1729
1730 BEAST_EXPECT(env.balance(alice, usd) == preAlice - delta);
1731 BEAST_EXPECT(env.balance(bob, usd) == usd(10'100));
1732 }
1733 // test rate change - higher
1734 {
1735 Env env{*this, features};
1736 auto const baseFee = env.current()->fees().base;
1737 env.fund(XRP(10'000), alice, bob, gw);
1738 env(fset(gw, asfAllowTrustLineLocking));
1739 env(rate(gw, 1.25));
1740 env.close();
1741 env.trust(usd(100'000), alice);
1742 env.trust(usd(100'000), bob);
1743 env.close();
1744 env(pay(gw, alice, usd(10'000)));
1745 env(pay(gw, bob, usd(10'000)));
1746 env.close();
1747
1748 // alice can create escrow w/ xfer rate
1749 auto const preAlice = env.balance(alice, usd);
1750 auto const seq1 = env.seq(alice);
1751 auto const delta = usd(125);
1752 env(escrow::create(alice, bob, delta),
1754 escrow::kFinishTime(env.now() + 1s),
1755 Fee(baseFee * 150));
1756 env.close();
1757 auto transferRate = escrow::rate(env, alice, seq1);
1758 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1759
1760 // issuer changes rate higher
1761 env(rate(gw, 1.26));
1762 env.close();
1763
1764 // bob can finish escrow - rate unchanged
1765 env(escrow::finish(bob, alice, seq1),
1768 Fee(baseFee * 150));
1769 env.close();
1770
1771 BEAST_EXPECT(env.balance(alice, usd) == preAlice - delta);
1772 BEAST_EXPECT(env.balance(bob, usd) == usd(10'100));
1773 }
1774
1775 // test rate change - lower
1776 {
1777 Env env{*this, features};
1778 auto const baseFee = env.current()->fees().base;
1779 env.fund(XRP(10'000), alice, bob, gw);
1780 env(fset(gw, asfAllowTrustLineLocking));
1781 env(rate(gw, 1.25));
1782 env.close();
1783 env.trust(usd(100'000), alice);
1784 env.trust(usd(100'000), bob);
1785 env.close();
1786 env(pay(gw, alice, usd(10'000)));
1787 env(pay(gw, bob, usd(10'000)));
1788 env.close();
1789
1790 // alice can create escrow w/ xfer rate
1791 auto const preAlice = env.balance(alice, usd);
1792 auto const seq1 = env.seq(alice);
1793 auto const delta = usd(125);
1794 env(escrow::create(alice, bob, delta),
1796 escrow::kFinishTime(env.now() + 1s),
1797 Fee(baseFee * 150));
1798 env.close();
1799 auto transferRate = escrow::rate(env, alice, seq1);
1800 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1801
1802 // issuer changes rate lower
1803 env(rate(gw, 1.00));
1804 env.close();
1805
1806 // bob can finish escrow - rate changed
1807 env(escrow::finish(bob, alice, seq1),
1810 Fee(baseFee * 150));
1811 env.close();
1812
1813 BEAST_EXPECT(env.balance(alice, usd) == preAlice - delta);
1814 BEAST_EXPECT(env.balance(bob, usd) == usd(10125));
1815 }
1816
1817 // test cancel doesn't charge rate
1818 {
1819 Env env{*this, features};
1820 auto const baseFee = env.current()->fees().base;
1821 env.fund(XRP(10'000), alice, bob, gw);
1822 env(fset(gw, asfAllowTrustLineLocking));
1823 env(rate(gw, 1.25));
1824 env.close();
1825 env.trust(usd(100'000), alice);
1826 env.trust(usd(100'000), bob);
1827 env.close();
1828 env(pay(gw, alice, usd(10'000)));
1829 env(pay(gw, bob, usd(10'000)));
1830 env.close();
1831
1832 // alice can create escrow w/ xfer rate
1833 auto const preAlice = env.balance(alice, usd);
1834 auto const seq1 = env.seq(alice);
1835 auto const delta = usd(125);
1836 env(escrow::create(alice, bob, delta),
1837 escrow::kFinishTime(env.now() + 1s),
1838 escrow::kCancelTime(env.now() + 3s),
1839 Fee(baseFee));
1840 env.close();
1841 auto transferRate = escrow::rate(env, alice, seq1);
1842 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1843
1844 // issuer changes rate lower
1845 env(rate(gw, 1.00));
1846 env.close();
1847
1848 // alice can cancel escrow - rate is not charged
1849 env(escrow::cancel(alice, alice, seq1), Fee(baseFee));
1850 env.close();
1851
1852 BEAST_EXPECT(env.balance(alice, usd) == preAlice);
1853 BEAST_EXPECT(env.balance(bob, usd) == usd(10000));
1854 }
1855 }
1856
1857 void
1859 {
1860 testcase("IOU Limit");
1861 using namespace test::jtx;
1862 using namespace std::literals;
1863
1864 auto const alice = Account("alice");
1865 auto const bob = Account("bob");
1866 auto const gw = Account{"gateway"};
1867 auto const usd = gw["USD"];
1868
1869 // test LimitAmount
1870 {
1871 Env env{*this, features};
1872 auto const baseFee = env.current()->fees().base;
1873 env.fund(XRP(1'000), alice, bob, gw);
1874 env(fset(gw, asfAllowTrustLineLocking));
1875 env.close();
1876 env.trust(usd(10'000), alice, bob);
1877 env.close();
1878 env(pay(gw, alice, usd(1'000)));
1879 env(pay(gw, bob, usd(1'000)));
1880 env.close();
1881
1882 // alice can create escrow
1883 auto seq1 = env.seq(alice);
1884 auto const delta = usd(125);
1885 env(escrow::create(alice, bob, delta),
1887 escrow::kFinishTime(env.now() + 1s),
1888 Fee(baseFee * 150));
1889 env.close();
1890
1891 // bob can finish
1892 auto const preBobLimit = env.limit(bob, usd);
1893 env(escrow::finish(bob, alice, seq1),
1896 Fee(baseFee * 150));
1897 env.close();
1898 auto const postBobLimit = env.limit(bob, usd);
1899 // bob's limit is NOT changed
1900 BEAST_EXPECT(postBobLimit == preBobLimit);
1901 }
1902 }
1903
1904 void
1906 {
1907 testcase("IOU Require Auth");
1908 using namespace test::jtx;
1909 using namespace std::literals;
1910
1911 auto const alice = Account("alice");
1912 auto const bob = Account("bob");
1913 auto const carol = Account("carol");
1914 auto const gw = Account{"gateway"};
1915 auto const usd = gw["USD"];
1916
1917 auto const aliceUSD = alice["USD"];
1918 auto const bobUSD = bob["USD"];
1919
1920 Env env{*this, features};
1921 auto const baseFee = env.current()->fees().base;
1922 env.fund(XRP(1'000), alice, bob, gw);
1923 env(fset(gw, asfAllowTrustLineLocking));
1924 env(fset(gw, asfRequireAuth));
1925 env.close();
1926 env(trust(gw, aliceUSD(10'000)), Txflags(tfSetfAuth));
1927 env(trust(alice, usd(10'000)));
1928 env(trust(bob, usd(10'000)));
1929 env.close();
1930 env(pay(gw, alice, usd(1'000)));
1931 env.close();
1932
1933 // alice cannot create escrow - fails without auth
1934 auto seq1 = env.seq(alice);
1935 auto const delta = usd(125);
1936 env(escrow::create(alice, bob, delta),
1938 escrow::kFinishTime(env.now() + 1s),
1939 Fee(baseFee * 150),
1940 Ter(tecNO_AUTH));
1941 env.close();
1942
1943 // set auth on bob
1944 env(trust(gw, bobUSD(10'000)), Txflags(tfSetfAuth));
1945 env(trust(bob, usd(10'000)));
1946 env.close();
1947 env(pay(gw, bob, usd(1'000)));
1948 env.close();
1949
1950 // alice can create escrow - bob has auth
1951 seq1 = env.seq(alice);
1952 env(escrow::create(alice, bob, delta),
1954 escrow::kFinishTime(env.now() + 1s),
1955 Fee(baseFee * 150));
1956 env.close();
1957
1958 // bob can finish
1959 env(escrow::finish(bob, alice, seq1),
1962 Fee(baseFee * 150));
1963 env.close();
1964 }
1965
1966 void
1968 {
1969 testcase("IOU Freeze");
1970 using namespace test::jtx;
1971 using namespace std::literals;
1972
1973 auto const alice = Account("alice");
1974 auto const bob = Account("bob");
1975 auto const carol = Account("carol");
1976 auto const gw = Account{"gateway"};
1977 auto const usd = gw["USD"];
1978
1979 // test Global Freeze
1980 {
1981 Env env{*this, features};
1982 auto const baseFee = env.current()->fees().base;
1983 env.fund(XRP(10'000), alice, bob, gw);
1984 env(fset(gw, asfAllowTrustLineLocking));
1985 env.close();
1986 env.trust(usd(100'000), alice);
1987 env.trust(usd(100'000), bob);
1988 env.close();
1989 env(pay(gw, alice, usd(10'000)));
1990 env(pay(gw, bob, usd(10'000)));
1991 env.close();
1992 env(fset(gw, asfGlobalFreeze));
1993 env.close();
1994
1995 // setup transaction
1996 auto seq1 = env.seq(alice);
1997 auto const delta = usd(125);
1998
1999 // create escrow fails - frozen trustline
2000 env(escrow::create(alice, bob, delta),
2002 escrow::kFinishTime(env.now() + 1s),
2003 Fee(baseFee * 150),
2004 Ter(tecFROZEN));
2005 env.close();
2006
2007 // clear global freeze
2008 env(fclear(gw, asfGlobalFreeze));
2009 env.close();
2010
2011 // create escrow success
2012 seq1 = env.seq(alice);
2013 env(escrow::create(alice, bob, delta),
2015 escrow::kFinishTime(env.now() + 1s),
2016 Fee(baseFee * 150));
2017 env.close();
2018
2019 // set global freeze
2020 env(fset(gw, asfGlobalFreeze));
2021 env.close();
2022
2023 // bob finish escrow success regardless of frozen assets
2024 env(escrow::finish(bob, alice, seq1),
2027 Fee(baseFee * 150));
2028 env.close();
2029
2030 // clear global freeze
2031 env(fclear(gw, asfGlobalFreeze));
2032 env.close();
2033
2034 // create escrow success
2035 seq1 = env.seq(alice);
2036 env(escrow::create(alice, bob, delta),
2038 escrow::kCancelTime(env.now() + 1s),
2039 Fee(baseFee * 150));
2040 env.close();
2041
2042 // set global freeze
2043 env(fset(gw, asfGlobalFreeze));
2044 env.close();
2045
2046 // bob cancel escrow success regardless of frozen assets
2047 env(escrow::cancel(bob, alice, seq1), Fee(baseFee));
2048 env.close();
2049 }
2050
2051 // test Individual Freeze
2052 {
2053 // Env Setup
2054 Env env{*this, features};
2055 auto const baseFee = env.current()->fees().base;
2056 env.fund(XRP(10'000), alice, bob, gw);
2057 env(fset(gw, asfAllowTrustLineLocking));
2058 env.close();
2059 env(trust(alice, usd(100'000)));
2060 env(trust(bob, usd(100'000)));
2061 env.close();
2062 env(pay(gw, alice, usd(10'000)));
2063 env(pay(gw, bob, usd(10'000)));
2064 env.close();
2065
2066 // set freeze on alice trustline
2067 env(trust(gw, usd(10'000), alice, tfSetFreeze));
2068 env.close();
2069
2070 // setup transaction
2071 auto seq1 = env.seq(alice);
2072 auto const delta = usd(125);
2073
2074 // create escrow fails - frozen trustline
2075 env(escrow::create(alice, bob, delta),
2077 escrow::kFinishTime(env.now() + 1s),
2078 Fee(baseFee * 150),
2079 Ter(tecFROZEN));
2080 env.close();
2081
2082 // clear freeze on alice trustline
2083 env(trust(gw, usd(10'000), alice, tfClearFreeze));
2084 env.close();
2085
2086 // create escrow success
2087 seq1 = env.seq(alice);
2088 env(escrow::create(alice, bob, delta),
2090 escrow::kFinishTime(env.now() + 1s),
2091 Fee(baseFee * 150));
2092 env.close();
2093
2094 // set freeze on bob trustline
2095 env(trust(gw, usd(10'000), bob, tfSetFreeze));
2096 env.close();
2097
2098 // bob finish escrow success regardless of frozen assets
2099 env(escrow::finish(bob, alice, seq1),
2102 Fee(baseFee * 150));
2103 env.close();
2104
2105 // reset freeze on bob and alice trustline
2106 env(trust(gw, usd(10'000), alice, tfClearFreeze));
2107 env(trust(gw, usd(10'000), bob, tfClearFreeze));
2108 env.close();
2109
2110 // create escrow success
2111 seq1 = env.seq(alice);
2112 env(escrow::create(alice, bob, delta),
2114 escrow::kCancelTime(env.now() + 1s),
2115 Fee(baseFee * 150));
2116 env.close();
2117
2118 // set freeze on bob trustline
2119 env(trust(gw, usd(10'000), bob, tfSetFreeze));
2120 env.close();
2121
2122 // bob cancel escrow success regardless of frozen assets
2123 env(escrow::cancel(bob, alice, seq1), Fee(baseFee));
2124 env.close();
2125 }
2126
2127 // test Deep Freeze
2128 {
2129 // Env Setup
2130 Env env{*this, features};
2131 auto const baseFee = env.current()->fees().base;
2132 env.fund(XRP(10'000), alice, bob, gw);
2133 env(fset(gw, asfAllowTrustLineLocking));
2134 env.close();
2135 env(trust(alice, usd(100'000)));
2136 env(trust(bob, usd(100'000)));
2137 env.close();
2138 env(pay(gw, alice, usd(10'000)));
2139 env(pay(gw, bob, usd(10'000)));
2140 env.close();
2141
2142 // set freeze on alice trustline
2143 env(trust(gw, usd(10'000), alice, tfSetFreeze | tfSetDeepFreeze));
2144 env.close();
2145
2146 // setup transaction
2147 auto seq1 = env.seq(alice);
2148 auto const delta = usd(125);
2149
2150 // create escrow fails - frozen trustline
2151 env(escrow::create(alice, bob, delta),
2153 escrow::kFinishTime(env.now() + 1s),
2154 Fee(baseFee * 150),
2155 Ter(tecFROZEN));
2156 env.close();
2157
2158 // clear freeze on alice trustline
2159 env(trust(gw, usd(10'000), alice, tfClearFreeze | tfClearDeepFreeze));
2160 env.close();
2161
2162 // create escrow success
2163 seq1 = env.seq(alice);
2164 env(escrow::create(alice, bob, delta),
2166 escrow::kFinishTime(env.now() + 1s),
2167 Fee(baseFee * 150));
2168 env.close();
2169
2170 // set freeze on bob trustline
2171 env(trust(gw, usd(10'000), bob, tfSetFreeze | tfSetDeepFreeze));
2172 env.close();
2173
2174 // bob finish escrow fails because of deep frozen assets
2175 env(escrow::finish(bob, alice, seq1),
2178 Fee(baseFee * 150),
2179 Ter(tecFROZEN));
2180 env.close();
2181
2182 // reset freeze on alice and bob trustline
2183 env(trust(gw, usd(10'000), alice, tfClearFreeze | tfClearDeepFreeze));
2184 env(trust(gw, usd(10'000), bob, tfClearFreeze | tfClearDeepFreeze));
2185 env.close();
2186
2187 // create escrow success
2188 seq1 = env.seq(alice);
2189 env(escrow::create(alice, bob, delta),
2191 escrow::kCancelTime(env.now() + 1s),
2192 Fee(baseFee * 150));
2193 env.close();
2194
2195 // set freeze on bob trustline
2196 env(trust(gw, usd(10'000), bob, tfSetFreeze | tfSetDeepFreeze));
2197 env.close();
2198
2199 // bob cancel escrow fails because of deep frozen assets
2200 env(escrow::cancel(bob, alice, seq1), Fee(baseFee), Ter(tesSUCCESS));
2201 env.close();
2202 }
2203 }
2204 void
2206 {
2207 testcase("IOU Insufficient Funds");
2208 using namespace test::jtx;
2209 using namespace std::literals;
2210
2211 auto const alice = Account("alice");
2212 auto const bob = Account("bob");
2213 auto const carol = Account("carol");
2214 auto const gw = Account{"gateway"};
2215 auto const usd = gw["USD"];
2216 {
2217 // test tecPATH_PARTIAL
2218 // ie. has 10'000, escrow 1'000 then try to pay 10'000
2219 Env env{*this, features};
2220 auto const baseFee = env.current()->fees().base;
2221 env.fund(XRP(10'000), alice, bob, gw);
2222 env(fset(gw, asfAllowTrustLineLocking));
2223 env.close();
2224 env.trust(usd(100'000), alice);
2225 env.trust(usd(100'000), bob);
2226 env.close();
2227 env(pay(gw, alice, usd(10'000)));
2228 env(pay(gw, bob, usd(10'000)));
2229 env.close();
2230
2231 // create escrow success
2232 auto const delta = usd(1'000);
2233 env(escrow::create(alice, bob, delta),
2235 escrow::kFinishTime(env.now() + 1s),
2236 Fee(baseFee * 150));
2237 env.close();
2238 env(pay(alice, gw, usd(10'000)), Ter(tecPATH_PARTIAL));
2239 }
2240 {
2241 // test tecINSUFFICIENT_FUNDS
2242 // ie. has 10'000 escrow 1'000 then try to escrow 10'000
2243 Env env{*this, features};
2244 auto const baseFee = env.current()->fees().base;
2245 env.fund(XRP(10'000), alice, bob, gw);
2246 env(fset(gw, asfAllowTrustLineLocking));
2247 env.close();
2248 env.trust(usd(100'000), alice);
2249 env.trust(usd(100'000), bob);
2250 env.close();
2251 env(pay(gw, alice, usd(10'000)));
2252 env(pay(gw, bob, usd(10'000)));
2253 env.close();
2254
2255 auto const delta = usd(1'000);
2256 env(escrow::create(alice, bob, delta),
2258 escrow::kFinishTime(env.now() + 1s),
2259 Fee(baseFee * 150));
2260 env.close();
2261
2262 env(escrow::create(alice, bob, usd(10'000)),
2264 escrow::kFinishTime(env.now() + 1s),
2265 Fee(baseFee * 150),
2267 env.close();
2268 }
2269 }
2270
2271 void
2273 {
2274 testcase("IOU Precision Loss");
2275 using namespace test::jtx;
2276 using namespace std::literals;
2277
2278 auto const alice = Account("alice");
2279 auto const bob = Account("bob");
2280 auto const gw = Account{"gateway"};
2281 auto const usd = gw["USD"];
2282
2283 // test min create precision loss
2284 {
2285 Env env(*this, features);
2286 auto const baseFee = env.current()->fees().base;
2287 env.fund(XRP(10'000), alice, bob, gw);
2288 env(fset(gw, asfAllowTrustLineLocking));
2289 env.close();
2290 env.trust(usd(100000000000000000), alice);
2291 env.trust(usd(100000000000000000), bob);
2292 env.close();
2293 env(pay(gw, alice, usd(10000000000000000)));
2294 env(pay(gw, bob, usd(1)));
2295 env.close();
2296
2297 bool const largeMantissa = features[featureSingleAssetVault] ||
2298 features[featureLendingProtocol] || features[featureMPTokensV2];
2299
2300 // alice cannot create escrow for 1/10 iou - precision loss
2301 env(escrow::create(alice, bob, usd(1)),
2303 escrow::kFinishTime(env.now() + 1s),
2304 Fee(baseFee * 150),
2305 Ter(largeMantissa ? (TER)tesSUCCESS : (TER)tecPRECISION_LOSS));
2306 env.close();
2307
2308 auto const seq1 = env.seq(alice);
2309 // alice can create escrow for 1'000 iou
2310 env(escrow::create(alice, bob, usd(1'000)),
2312 escrow::kFinishTime(env.now() + 1s),
2313 Fee(baseFee * 150));
2314 env.close();
2315
2316 // bob finish escrow success
2317 env(escrow::finish(bob, alice, seq1),
2320 Fee(baseFee * 150));
2321 env.close();
2322 }
2323 }
2324
2325 void
2327 {
2328 testcase("MPT Enablement");
2329
2330 using namespace jtx;
2331 using namespace std::chrono;
2332
2333 for (bool const withTokenEscrow : {false, true})
2334 {
2335 auto const amend = withTokenEscrow ? features : features - featureTokenEscrow;
2336 Env env{*this, amend};
2337 auto const baseFee = env.current()->fees().base;
2338 auto const alice = Account("alice");
2339 auto const bob = Account("bob");
2340 auto const gw = Account("gw");
2341 env.fund(XRP(5000), bob);
2342
2343 MPTTester mptGw(env, gw, {.holders = {alice}});
2344 mptGw.create(
2345 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2346 mptGw.authorize({.account = alice});
2347 auto const mpt = mptGw["MPT"];
2348 env(pay(gw, alice, mpt(10'000)));
2349 env.close();
2350
2351 auto const createResult = withTokenEscrow ? Ter(tesSUCCESS) : Ter(temBAD_AMOUNT);
2352 auto const finishResult = withTokenEscrow ? Ter(tesSUCCESS) : Ter(tecNO_TARGET);
2353
2354 auto const seq1 = env.seq(alice);
2355 env(escrow::create(alice, bob, mpt(1'000)),
2357 escrow::kFinishTime(env.now() + 1s),
2358 Fee(baseFee * 150),
2359 createResult);
2360 env.close();
2361 env(escrow::finish(bob, alice, seq1),
2364 Fee(baseFee * 150),
2365 finishResult);
2366 env.close();
2367 auto const seq2 = env.seq(alice);
2368 env(escrow::create(alice, bob, mpt(1'000)),
2370 escrow::kFinishTime(env.now() + 1s),
2371 escrow::kCancelTime(env.now() + 2s),
2372 Fee(baseFee * 150),
2373 createResult);
2374 env.close();
2375 env(escrow::cancel(bob, alice, seq2), finishResult);
2376 env.close();
2377 }
2378 }
2379
2380 void
2382 {
2383 testcase("MPT Create Preflight");
2384 using namespace test::jtx;
2385 using namespace std::literals;
2386
2387 for (bool const withMPT : {true, false})
2388 {
2389 auto const amend = withMPT ? features : features - featureMPTokensV1;
2390 Env env{*this, amend};
2391 auto const baseFee = env.current()->fees().base;
2392 auto const alice = Account("alice");
2393 auto const bob = Account("bob");
2394 auto const gw = Account("gw");
2395 env.fund(XRP(1'000), alice, bob, gw);
2396
2397 json::Value jv = escrow::create(alice, bob, XRP(1));
2398 jv.removeMember(jss::Amount);
2399 jv[jss::Amount][jss::mpt_issuance_id] =
2400 "00000004A407AF5856CCF3C42619DAA925813FC955C72983";
2401 jv[jss::Amount][jss::value] = "-1";
2402
2403 auto const result = withMPT ? Ter(temBAD_AMOUNT) : Ter(temDISABLED);
2404 env(jv,
2406 escrow::kFinishTime(env.now() + 1s),
2407 Fee(baseFee * 150),
2408 result);
2409 env.close();
2410 }
2411
2412 // temBAD_AMOUNT: amount < 0
2413 {
2414 Env env{*this, features};
2415 auto const baseFee = env.current()->fees().base;
2416 auto const alice = Account("alice");
2417 auto const bob = Account("bob");
2418 auto const gw = Account("gw");
2419
2420 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2421 mptGw.create(
2422 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2423 mptGw.authorize({.account = alice});
2424 mptGw.authorize({.account = bob});
2425 auto const mpt = mptGw["MPT"];
2426 env(pay(gw, alice, mpt(10'000)));
2427 env(pay(gw, bob, mpt(10'000)));
2428 env.close();
2429
2430 env(escrow::create(alice, bob, mpt(-1)),
2432 escrow::kFinishTime(env.now() + 1s),
2433 Fee(baseFee * 150),
2435 env.close();
2436 }
2437 }
2438
2439 void
2441 {
2442 testcase("MPT Create Preclaim");
2443 using namespace test::jtx;
2444 using namespace std::literals;
2445
2446 // tecNO_PERMISSION: issuer is the same as the account
2447 {
2448 Env env{*this, features};
2449 auto const baseFee = env.current()->fees().base;
2450 auto const alice = Account("alice");
2451 auto const gw = Account("gw");
2452
2453 MPTTester mptGw(env, gw, {.holders = {alice}});
2454 mptGw.create(
2455 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2456 mptGw.authorize({.account = alice});
2457 auto const mpt = mptGw["MPT"];
2458 env(pay(gw, alice, mpt(10'000)));
2459 env.close();
2460
2461 env(escrow::create(gw, alice, mpt(1)),
2463 escrow::kFinishTime(env.now() + 1s),
2464 Fee(baseFee * 150),
2466 env.close();
2467 }
2468
2469 // tecOBJECT_NOT_FOUND: mpt does not exist
2470 {
2471 Env env{*this, features};
2472 auto const baseFee = env.current()->fees().base;
2473 auto const alice = Account("alice");
2474 auto const bob = Account("bob");
2475 auto const gw = Account("gw");
2476 env.fund(XRP(10'000), alice, bob, gw);
2477 env.close();
2478
2479 auto const mpt = xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice));
2480 json::Value jv = escrow::create(alice, bob, mpt(2));
2481 jv[jss::Amount][jss::mpt_issuance_id] =
2482 "00000004A407AF5856CCF3C42619DAA925813FC955C72983";
2483 env(jv,
2485 escrow::kFinishTime(env.now() + 1s),
2486 Fee(baseFee * 150),
2488 env.close();
2489 }
2490
2491 // tecNO_PERMISSION: tfMPTCanEscrow is not enabled
2492 {
2493 Env env{*this, features};
2494 auto const baseFee = env.current()->fees().base;
2495 auto const alice = Account("alice");
2496 auto const bob = Account("bob");
2497 auto const gw = Account("gw");
2498
2499 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2500 mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
2501 mptGw.authorize({.account = alice});
2502 mptGw.authorize({.account = bob});
2503 auto const mpt = mptGw["MPT"];
2504 env(pay(gw, alice, mpt(10'000)));
2505 env(pay(gw, bob, mpt(10'000)));
2506 env.close();
2507
2508 env(escrow::create(alice, bob, mpt(3)),
2510 escrow::kFinishTime(env.now() + 1s),
2511 Fee(baseFee * 150),
2513 env.close();
2514 }
2515
2516 // tecOBJECT_NOT_FOUND: account does not have the mpt
2517 {
2518 Env env{*this, features};
2519 auto const baseFee = env.current()->fees().base;
2520 auto const alice = Account("alice");
2521 auto const bob = Account("bob");
2522 auto const gw = Account("gw");
2523
2524 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2525 mptGw.create(
2526 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2527 auto const mpt = mptGw["MPT"];
2528
2529 env(escrow::create(alice, bob, mpt(4)),
2531 escrow::kFinishTime(env.now() + 1s),
2532 Fee(baseFee * 150),
2534 env.close();
2535 }
2536
2537 // tecNO_AUTH: requireAuth set: account not authorized
2538 {
2539 Env env{*this, features};
2540 auto const baseFee = env.current()->fees().base;
2541 auto const alice = Account("alice");
2542 auto const bob = Account("bob");
2543 auto const gw = Account("gw");
2544
2545 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2546 mptGw.create(
2547 {.ownerCount = 1,
2548 .holderCount = 0,
2549 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2550 mptGw.authorize({.account = alice});
2551 mptGw.authorize({.account = gw, .holder = alice});
2552 auto const mpt = mptGw["MPT"];
2553 env(pay(gw, alice, mpt(10'000)));
2554 env.close();
2555
2556 // unauthorize account
2557 mptGw.authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
2558
2559 env(escrow::create(alice, bob, mpt(5)),
2561 escrow::kFinishTime(env.now() + 1s),
2562 Fee(baseFee * 150),
2563 Ter(tecNO_AUTH));
2564 env.close();
2565 }
2566
2567 // tecNO_AUTH: requireAuth set: dest not authorized
2568 {
2569 Env env{*this, features};
2570 auto const baseFee = env.current()->fees().base;
2571 auto const alice = Account("alice");
2572 auto const bob = Account("bob");
2573 auto const gw = Account("gw");
2574
2575 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2576 mptGw.create(
2577 {.ownerCount = 1,
2578 .holderCount = 0,
2579 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2580 mptGw.authorize({.account = alice});
2581 mptGw.authorize({.account = gw, .holder = alice});
2582 mptGw.authorize({.account = bob});
2583 mptGw.authorize({.account = gw, .holder = bob});
2584 auto const mpt = mptGw["MPT"];
2585 env(pay(gw, alice, mpt(10'000)));
2586 env(pay(gw, bob, mpt(10'000)));
2587 env.close();
2588
2589 // unauthorize dest
2590 mptGw.authorize({.account = gw, .holder = bob, .flags = tfMPTUnauthorize});
2591
2592 env(escrow::create(alice, bob, mpt(6)),
2594 escrow::kFinishTime(env.now() + 1s),
2595 Fee(baseFee * 150),
2596 Ter(tecNO_AUTH));
2597 env.close();
2598 }
2599
2600 // tecLOCKED: issuer has locked the account
2601 {
2602 Env env{*this, features};
2603 auto const baseFee = env.current()->fees().base;
2604 auto const alice = Account("alice");
2605 auto const bob = Account("bob");
2606 auto const gw = Account("gw");
2607
2608 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2609 mptGw.create(
2610 {.ownerCount = 1,
2611 .holderCount = 0,
2612 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
2613 mptGw.authorize({.account = alice});
2614 mptGw.authorize({.account = bob});
2615 auto const mpt = mptGw["MPT"];
2616 env(pay(gw, alice, mpt(10'000)));
2617 env(pay(gw, bob, mpt(10'000)));
2618 env.close();
2619
2620 // lock account
2621 mptGw.set({.account = gw, .holder = alice, .flags = tfMPTLock});
2622
2623 env(escrow::create(alice, bob, mpt(7)),
2625 escrow::kFinishTime(env.now() + 1s),
2626 Fee(baseFee * 150),
2627 Ter(tecLOCKED));
2628 env.close();
2629 }
2630
2631 // tecLOCKED: issuer has locked the dest
2632 {
2633 Env env{*this, features};
2634 auto const baseFee = env.current()->fees().base;
2635 auto const alice = Account("alice");
2636 auto const bob = Account("bob");
2637 auto const gw = Account("gw");
2638
2639 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2640 mptGw.create(
2641 {.ownerCount = 1,
2642 .holderCount = 0,
2643 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
2644 mptGw.authorize({.account = alice});
2645 mptGw.authorize({.account = bob});
2646 auto const mpt = mptGw["MPT"];
2647 env(pay(gw, alice, mpt(10'000)));
2648 env(pay(gw, bob, mpt(10'000)));
2649 env.close();
2650
2651 // lock dest
2652 mptGw.set({.account = gw, .holder = bob, .flags = tfMPTLock});
2653
2654 env(escrow::create(alice, bob, mpt(8)),
2656 escrow::kFinishTime(env.now() + 1s),
2657 Fee(baseFee * 150),
2658 Ter(tecLOCKED));
2659 env.close();
2660 }
2661
2662 // tecNO_AUTH: mpt cannot be transferred
2663 {
2664 Env env{*this, features};
2665 auto const baseFee = env.current()->fees().base;
2666 auto const alice = Account("alice");
2667 auto const bob = Account("bob");
2668 auto const gw = Account("gw");
2669
2670 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2671 mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow});
2672 mptGw.authorize({.account = alice});
2673 mptGw.authorize({.account = bob});
2674 auto const mpt = mptGw["MPT"];
2675 env(pay(gw, alice, mpt(10'000)));
2676 env(pay(gw, bob, mpt(10'000)));
2677 env.close();
2678
2679 env(escrow::create(alice, bob, mpt(9)),
2681 escrow::kFinishTime(env.now() + 1s),
2682 Fee(baseFee * 150),
2683 Ter(tecNO_AUTH));
2684 env.close();
2685 }
2686
2687 // tecINSUFFICIENT_FUNDS: spendable amount is zero
2688 {
2689 Env env{*this, features};
2690 auto const baseFee = env.current()->fees().base;
2691 auto const alice = Account("alice");
2692 auto const bob = Account("bob");
2693 auto const gw = Account("gw");
2694
2695 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2696 mptGw.create(
2697 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2698 mptGw.authorize({.account = alice});
2699 mptGw.authorize({.account = bob});
2700 auto const mpt = mptGw["MPT"];
2701 env(pay(gw, bob, mpt(10)));
2702 env.close();
2703
2704 env(escrow::create(alice, bob, mpt(11)),
2706 escrow::kFinishTime(env.now() + 1s),
2707 Fee(baseFee * 150),
2709 env.close();
2710 }
2711
2712 // tecINSUFFICIENT_FUNDS: spendable amount is less than the amount
2713 {
2714 Env env{*this, features};
2715 auto const baseFee = env.current()->fees().base;
2716 auto const alice = Account("alice");
2717 auto const bob = Account("bob");
2718 auto const gw = Account("gw");
2719
2720 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2721 mptGw.create(
2722 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2723 mptGw.authorize({.account = alice});
2724 mptGw.authorize({.account = bob});
2725 auto const mpt = mptGw["MPT"];
2726 env(pay(gw, alice, mpt(10)));
2727 env(pay(gw, bob, mpt(10)));
2728 env.close();
2729
2730 env(escrow::create(alice, bob, mpt(11)),
2732 escrow::kFinishTime(env.now() + 1s),
2733 Fee(baseFee * 150),
2735 env.close();
2736 }
2737 }
2738
2739 void
2741 {
2742 testcase("MPT Finish Preclaim");
2743 using namespace test::jtx;
2744 using namespace std::literals;
2745
2746 // tecNO_AUTH: requireAuth set: dest not authorized
2747 {
2748 Env env{*this, features};
2749 auto const baseFee = env.current()->fees().base;
2750 auto const alice = Account("alice");
2751 auto const bob = Account("bob");
2752 auto const gw = Account("gw");
2753
2754 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2755 mptGw.create(
2756 {.ownerCount = 1,
2757 .holderCount = 0,
2758 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2759 mptGw.authorize({.account = alice});
2760 mptGw.authorize({.account = gw, .holder = alice});
2761 mptGw.authorize({.account = bob});
2762 mptGw.authorize({.account = gw, .holder = bob});
2763 auto const mpt = mptGw["MPT"];
2764 env(pay(gw, alice, mpt(10'000)));
2765 env(pay(gw, bob, mpt(10'000)));
2766 env.close();
2767
2768 auto const seq1 = env.seq(alice);
2769 env(escrow::create(alice, bob, mpt(10)),
2771 escrow::kFinishTime(env.now() + 1s),
2772 Fee(baseFee * 150),
2773 Ter(tesSUCCESS));
2774 env.close();
2775
2776 // unauthorize dest
2777 mptGw.authorize({.account = gw, .holder = bob, .flags = tfMPTUnauthorize});
2778
2779 env(escrow::finish(bob, alice, seq1),
2782 Fee(baseFee * 150),
2783 Ter(tecNO_AUTH));
2784 env.close();
2785 }
2786
2787 // tecOBJECT_NOT_FOUND: MPT issuance does not exist
2788 {
2789 Env env{*this, features};
2790 auto const baseFee = env.current()->fees().base;
2791 auto const alice = Account("alice");
2792 auto const bob = Account("bob");
2793 env.fund(XRP(10'000), alice, bob);
2794 env.close();
2795
2796 auto const seq1 = env.seq(alice);
2797 env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
2798 Sandbox sb(&view, TapNone);
2799 auto sleNew =
2801 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
2802 STAmount const amt(mpt, 10);
2803 sleNew->setAccountID(sfDestination, bob);
2804 sleNew->setFieldAmount(sfAmount, amt);
2805 sb.insert(sleNew);
2806 sb.apply(view);
2807 return true;
2808 });
2809
2810 env(escrow::finish(bob, alice, seq1),
2813 Fee(baseFee * 150),
2815 env.close();
2816 }
2817
2818 // tecLOCKED: issuer has locked the dest
2819 {
2820 Env env{*this, features};
2821 auto const baseFee = env.current()->fees().base;
2822 auto const alice = Account("alice");
2823 auto const bob = Account("bob");
2824 auto const gw = Account("gw");
2825
2826 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2827 mptGw.create(
2828 {.ownerCount = 1,
2829 .holderCount = 0,
2830 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
2831 mptGw.authorize({.account = alice});
2832 mptGw.authorize({.account = bob});
2833 auto const mpt = mptGw["MPT"];
2834 env(pay(gw, alice, mpt(10'000)));
2835 env(pay(gw, bob, mpt(10'000)));
2836 env.close();
2837
2838 auto const seq1 = env.seq(alice);
2839 env(escrow::create(alice, bob, mpt(8)),
2841 escrow::kFinishTime(env.now() + 1s),
2842 Fee(baseFee * 150),
2843 Ter(tesSUCCESS));
2844 env.close();
2845
2846 // lock dest
2847 mptGw.set({.account = gw, .holder = bob, .flags = tfMPTLock});
2848
2849 env(escrow::finish(bob, alice, seq1),
2852 Fee(baseFee * 150),
2853 Ter(tecLOCKED));
2854 env.close();
2855 }
2856 }
2857
2858 void
2860 {
2861 testcase("MPT Finish Do Apply");
2862 using namespace test::jtx;
2863 using namespace std::literals;
2864
2865 // tecINSUFFICIENT_RESERVE: insufficient reserve to create MPT
2866 {
2867 Env env{*this, features};
2868 auto const baseFee = env.current()->fees().base;
2869 auto const acctReserve = env.current()->fees().reserve;
2870 auto const incReserve = env.current()->fees().increment;
2871
2872 auto const alice = Account("alice");
2873 auto const bob = Account("bob");
2874 auto const gw = Account("gw");
2875 env.fund(acctReserve + (incReserve - 1), bob);
2876 env.close();
2877
2878 MPTTester mptGw(env, gw, {.holders = {alice}});
2879 mptGw.create(
2880 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2881 mptGw.authorize({.account = alice});
2882 auto const mpt = mptGw["MPT"];
2883 env(pay(gw, alice, mpt(10'000)));
2884 env.close();
2885
2886 auto const seq1 = env.seq(alice);
2887 env(escrow::create(alice, bob, mpt(10)),
2889 escrow::kFinishTime(env.now() + 1s),
2890 Fee(baseFee * 150),
2891 Ter(tesSUCCESS));
2892 env.close();
2893
2894 env(escrow::finish(bob, alice, seq1),
2897 Fee(baseFee * 150),
2899 env.close();
2900 }
2901
2902 // tesSUCCESS: bob submits; finish MPT created
2903 {
2904 Env env{*this, features};
2905 auto const baseFee = env.current()->fees().base;
2906 auto const alice = Account("alice");
2907 auto const bob = Account("bob");
2908 auto const gw = Account("gw");
2909 env.fund(XRP(10'000), bob);
2910 env.close();
2911
2912 MPTTester mptGw(env, gw, {.holders = {alice}});
2913 mptGw.create(
2914 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2915 mptGw.authorize({.account = alice});
2916 auto const mpt = mptGw["MPT"];
2917 env(pay(gw, alice, mpt(10'000)));
2918 env.close();
2919
2920 auto const seq1 = env.seq(alice);
2921 env(escrow::create(alice, bob, mpt(10)),
2923 escrow::kFinishTime(env.now() + 1s),
2924 Fee(baseFee * 150),
2925 Ter(tesSUCCESS));
2926 env.close();
2927
2928 env(escrow::finish(bob, alice, seq1),
2931 Fee(baseFee * 150),
2932 Ter(tesSUCCESS));
2933 env.close();
2934 }
2935
2936 // tecNO_PERMISSION: carol submits; finish MPT not created
2937 {
2938 Env env{*this, features};
2939 auto const baseFee = env.current()->fees().base;
2940 auto const alice = Account("alice");
2941 auto const bob = Account("bob");
2942 auto const carol = Account("carol");
2943 auto const gw = Account("gw");
2944 env.fund(XRP(10'000), bob, carol);
2945 env.close();
2946
2947 MPTTester mptGw(env, gw, {.holders = {alice}});
2948 mptGw.create(
2949 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2950 mptGw.authorize({.account = alice});
2951 auto const mpt = mptGw["MPT"];
2952 env(pay(gw, alice, mpt(10'000)));
2953 env.close();
2954
2955 auto const seq1 = env.seq(alice);
2956 env(escrow::create(alice, bob, mpt(10)),
2958 escrow::kFinishTime(env.now() + 1s),
2959 Fee(baseFee * 150),
2960 Ter(tesSUCCESS));
2961 env.close();
2962
2963 env(escrow::finish(carol, alice, seq1),
2966 Fee(baseFee * 150),
2968 env.close();
2969 }
2970 }
2971
2972 void
2974 {
2975 testcase("MPT Cancel Preclaim");
2976 using namespace test::jtx;
2977 using namespace std::literals;
2978
2979 // tecNO_AUTH: requireAuth set: account not authorized
2980 {
2981 Env env{*this, features};
2982 auto const baseFee = env.current()->fees().base;
2983 auto const alice = Account("alice");
2984 auto const bob = Account("bob");
2985 auto const gw = Account("gw");
2986
2987 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2988 mptGw.create(
2989 {.ownerCount = 1,
2990 .holderCount = 0,
2991 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2992 mptGw.authorize({.account = alice});
2993 mptGw.authorize({.account = gw, .holder = alice});
2994 mptGw.authorize({.account = bob});
2995 mptGw.authorize({.account = gw, .holder = bob});
2996 auto const mpt = mptGw["MPT"];
2997 env(pay(gw, alice, mpt(10'000)));
2998 env(pay(gw, bob, mpt(10'000)));
2999 env.close();
3000
3001 auto const seq1 = env.seq(alice);
3002 env(escrow::create(alice, bob, mpt(10)),
3003 escrow::kCancelTime(env.now() + 2s),
3005 Fee(baseFee * 150),
3006 Ter(tesSUCCESS));
3007 env.close();
3008
3009 // unauthorize account
3010 mptGw.authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
3011
3012 env(escrow::cancel(bob, alice, seq1), Ter(tecNO_AUTH));
3013 env.close();
3014 }
3015
3016 // tecOBJECT_NOT_FOUND: MPT issuance does not exist
3017 {
3018 Env env{*this, features};
3019 auto const baseFee = env.current()->fees().base;
3020 auto const alice = Account("alice");
3021 auto const bob = Account("bob");
3022 env.fund(XRP(10'000), alice, bob);
3023
3024 auto const seq1 = env.seq(alice);
3025 env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
3026 Sandbox sb(&view, TapNone);
3027 auto sleNew =
3029 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
3030 STAmount const amt(mpt, 10);
3031 sleNew->setAccountID(sfDestination, bob);
3032 sleNew->setFieldAmount(sfAmount, amt);
3033 sb.insert(sleNew);
3034 sb.apply(view);
3035 return true;
3036 });
3037
3038 env(escrow::cancel(bob, alice, seq1), Fee(baseFee), Ter(tecOBJECT_NOT_FOUND));
3039 env.close();
3040 }
3041 }
3042
3043 void
3045 {
3046 testcase("MPT Balances");
3047
3048 using namespace jtx;
3049 using namespace std::chrono;
3050
3051 Env env{*this, features};
3052 auto const baseFee = env.current()->fees().base;
3053 auto const alice = Account("alice");
3054 auto const bob = Account("bob");
3055 auto const carol = Account("carol");
3056 auto const gw = Account("gw");
3057 env.fund(XRP(5000), bob);
3058
3059 MPTTester mptGw(env, gw, {.holders = {alice, carol}});
3060 mptGw.create(
3061 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3062 mptGw.authorize({.account = alice});
3063 mptGw.authorize({.account = carol});
3064 auto const mpt = mptGw["MPT"];
3065 env(pay(gw, alice, mpt(10'000)));
3066 env(pay(gw, carol, mpt(10'000)));
3067 env.close();
3068
3069 auto outstandingMPT = env.balance(gw, mpt);
3070
3071 // Create & Finish Escrow
3072 auto const seq1 = env.seq(alice);
3073 {
3074 auto const preAliceMPT = env.balance(alice, mpt);
3075 auto const preBobMPT = env.balance(bob, mpt);
3076 env(escrow::create(alice, bob, mpt(1'000)),
3078 escrow::kFinishTime(env.now() + 1s),
3079 Fee(baseFee * 150),
3080 Ter(tesSUCCESS));
3081 env.close();
3082
3083 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3084 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3085 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3086 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3087 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3088 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
3089 }
3090 {
3091 auto const preAliceMPT = env.balance(alice, mpt);
3092 auto const preBobMPT = env.balance(bob, mpt);
3093 env(escrow::finish(bob, alice, seq1),
3096 Fee(baseFee * 150),
3097 Ter(tesSUCCESS));
3098 env.close();
3099
3100 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT);
3101 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3102 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT + mpt(1'000));
3103 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3104 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3105 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3106 }
3107
3108 // Create & Cancel Escrow
3109 auto const seq2 = env.seq(alice);
3110 {
3111 auto const preAliceMPT = env.balance(alice, mpt);
3112 auto const preBobMPT = env.balance(bob, mpt);
3113 env(escrow::create(alice, bob, mpt(1'000)),
3115 escrow::kFinishTime(env.now() + 1s),
3116 escrow::kCancelTime(env.now() + 2s),
3117 Fee(baseFee * 150),
3118 Ter(tesSUCCESS));
3119 env.close();
3120
3121 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3122 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3123 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3124 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3125 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3126 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
3127 }
3128 {
3129 auto const preAliceMPT = env.balance(alice, mpt);
3130 auto const preBobMPT = env.balance(bob, mpt);
3131 env(escrow::cancel(bob, alice, seq2), Ter(tesSUCCESS));
3132 env.close();
3133
3134 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT + mpt(1'000));
3135 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3136 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3137 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3138 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3139 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3140 }
3141
3142 // Self Escrow Create & Finish
3143 {
3144 auto const seq = env.seq(alice);
3145 auto const preAliceMPT = env.balance(alice, mpt);
3146 env(escrow::create(alice, alice, mpt(1'000)),
3148 escrow::kFinishTime(env.now() + 1s),
3149 Fee(baseFee * 150),
3150 Ter(tesSUCCESS));
3151 env.close();
3152
3153 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3154 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3155 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3156 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
3157
3158 env(escrow::finish(alice, alice, seq),
3161 Fee(baseFee * 150),
3162 Ter(tesSUCCESS));
3163 env.close();
3164
3165 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT);
3166 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3167 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3168 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3169 }
3170
3171 // Self Escrow Create & Cancel
3172 {
3173 auto const seq = env.seq(alice);
3174 auto const preAliceMPT = env.balance(alice, mpt);
3175 env(escrow::create(alice, alice, mpt(1'000)),
3177 escrow::kFinishTime(env.now() + 1s),
3178 escrow::kCancelTime(env.now() + 2s),
3179 Fee(baseFee * 150),
3180 Ter(tesSUCCESS));
3181 env.close();
3182
3183 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3184 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3185 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3186 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
3187
3188 env(escrow::cancel(alice, alice, seq), Ter(tesSUCCESS));
3189 env.close();
3190
3191 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT);
3192 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3193 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3194 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3195 }
3196
3197 // Multiple Escrows
3198 {
3199 auto const preAliceMPT = env.balance(alice, mpt);
3200 auto const preBobMPT = env.balance(bob, mpt);
3201 auto const preCarolMPT = env.balance(carol, mpt);
3202 env(escrow::create(alice, bob, mpt(1'000)),
3204 escrow::kFinishTime(env.now() + 1s),
3205 Fee(baseFee * 150),
3206 Ter(tesSUCCESS));
3207 env.close();
3208
3209 env(escrow::create(carol, bob, mpt(1'000)),
3211 escrow::kFinishTime(env.now() + 1s),
3212 Fee(baseFee * 150),
3213 Ter(tesSUCCESS));
3214 env.close();
3215
3216 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3217 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3218 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3219 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3220 BEAST_EXPECT(env.balance(carol, mpt) == preCarolMPT - mpt(1'000));
3221 BEAST_EXPECT(mptEscrowed(env, carol, mpt) == 1'000);
3222 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3223 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 2'000);
3224 }
3225
3226 // Max MPT Amount Issued (Escrow 1 MPT)
3227 {
3228 Env env{*this, features};
3229 auto const baseFee = env.current()->fees().base;
3230 auto const alice = Account("alice");
3231 auto const bob = Account("bob");
3232 auto const gw = Account("gw");
3233
3234 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3235 mptGw.create(
3236 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3237 mptGw.authorize({.account = alice});
3238 mptGw.authorize({.account = bob});
3239 auto const mpt = mptGw["MPT"];
3240 env(pay(gw, alice, mpt(kMaxMpTokenAmount)));
3241 env.close();
3242
3243 auto const preAliceMPT = env.balance(alice, mpt);
3244 auto const preBobMPT = env.balance(bob, mpt);
3245 auto const outstandingMPT = env.balance(gw, mpt);
3246
3247 auto const seq1 = env.seq(alice);
3248 env(escrow::create(alice, bob, mpt(1)),
3250 escrow::kFinishTime(env.now() + 1s),
3251 Fee(baseFee * 150));
3252 env.close();
3253
3254 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1));
3255 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1);
3256 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3257 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3258 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3259 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1);
3260
3261 env(escrow::finish(bob, alice, seq1),
3264 Fee(baseFee * 150),
3265 Ter(tesSUCCESS));
3266 env.close();
3267
3268 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1));
3269 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3270 BEAST_EXPECT(
3271 !env.le(keylet::mptoken(mpt.mpt(), alice))->isFieldPresent(sfLockedAmount));
3272 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT + mpt(1));
3273 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3274 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3275 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3276 BEAST_EXPECT(
3277 !env.le(keylet::mptokenIssuance(mpt.mpt()))->isFieldPresent(sfLockedAmount));
3278 }
3279
3280 // Max MPT Amount Issued (Escrow Max MPT)
3281 {
3282 Env env{*this, features};
3283 auto const baseFee = env.current()->fees().base;
3284 auto const alice = Account("alice");
3285 auto const bob = Account("bob");
3286 auto const gw = Account("gw");
3287
3288 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3289 mptGw.create(
3290 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3291 mptGw.authorize({.account = alice});
3292 mptGw.authorize({.account = bob});
3293 auto const mpt = mptGw["MPT"];
3294 env(pay(gw, alice, mpt(kMaxMpTokenAmount)));
3295 env.close();
3296
3297 auto const preAliceMPT = env.balance(alice, mpt);
3298 auto const preBobMPT = env.balance(bob, mpt);
3299 auto const outstandingMPT = env.balance(gw, mpt);
3300
3301 // Escrow Max MPT - 10
3302 auto const seq1 = env.seq(alice);
3303 env(escrow::create(alice, bob, mpt(kMaxMpTokenAmount - 10)),
3305 escrow::kFinishTime(env.now() + 1s),
3306 Fee(baseFee * 150));
3307 env.close();
3308
3309 // Escrow 10 MPT
3310 auto const seq2 = env.seq(alice);
3311 env(escrow::create(alice, bob, mpt(10)),
3313 escrow::kFinishTime(env.now() + 1s),
3314 Fee(baseFee * 150));
3315 env.close();
3316
3317 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(kMaxMpTokenAmount));
3318 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == kMaxMpTokenAmount);
3319 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3320 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3321 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3322 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == kMaxMpTokenAmount);
3323
3324 env(escrow::finish(bob, alice, seq1),
3327 Fee(baseFee * 150),
3328 Ter(tesSUCCESS));
3329 env.close();
3330
3331 env(escrow::finish(bob, alice, seq2),
3334 Fee(baseFee * 150),
3335 Ter(tesSUCCESS));
3336 env.close();
3337
3338 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(kMaxMpTokenAmount));
3339 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3340 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT + mpt(kMaxMpTokenAmount));
3341 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3342 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3343 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3344 }
3345 }
3346
3347 void
3349 {
3350 using namespace jtx;
3351 using namespace std::chrono;
3352
3353 auto const alice = Account("alice");
3354 auto const bob = Account("bob");
3355 auto const carol = Account("carol");
3356 auto const gw = Account{"gateway"};
3357 {
3358 testcase("MPT Metadata to self");
3359
3360 Env env{*this, features};
3361 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3362 mptGw.create(
3363 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3364 mptGw.authorize({.account = alice});
3365 mptGw.authorize({.account = bob});
3366 auto const mpt = mptGw["MPT"];
3367 env(pay(gw, alice, mpt(10'000)));
3368 env(pay(gw, bob, mpt(10'000)));
3369 env.close();
3370 auto const aseq = env.seq(alice);
3371 auto const bseq = env.seq(bob);
3372
3373 env(escrow::create(alice, alice, mpt(1'000)),
3374 escrow::kFinishTime(env.now() + 1s),
3375 escrow::kCancelTime(env.now() + 500s));
3376 BEAST_EXPECT(
3377 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3378 env.close(5s);
3379 auto const aa = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
3380 BEAST_EXPECT(aa);
3381 {
3382 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3383 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
3384 BEAST_EXPECT(
3385 // NOLINTNEXTLINE(modernize-use-ranges)
3386 std::find(aod.begin(), aod.end(), aa) != aod.end());
3387 }
3388
3389 {
3390 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
3391 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 1);
3392 BEAST_EXPECT(
3393 // NOLINTNEXTLINE(modernize-use-ranges)
3394 std::find(iod.begin(), iod.end(), aa) == iod.end());
3395 }
3396
3397 env(escrow::create(bob, bob, mpt(1'000)),
3398 escrow::kFinishTime(env.now() + 1s),
3399 escrow::kCancelTime(env.now() + 2s));
3400 BEAST_EXPECT(
3401 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3402 env.close(5s);
3403 auto const bb = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
3404 BEAST_EXPECT(bb);
3405
3406 {
3407 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3408 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
3409 BEAST_EXPECT(
3410 // NOLINTNEXTLINE(modernize-use-ranges)
3411 std::find(bod.begin(), bod.end(), bb) != bod.end());
3412 }
3413
3414 env.close(5s);
3415 env(escrow::finish(alice, alice, aseq));
3416 {
3417 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
3418 BEAST_EXPECT(
3419 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3420
3421 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3422 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
3423 BEAST_EXPECT(
3424 // NOLINTNEXTLINE(modernize-use-ranges)
3425 std::find(aod.begin(), aod.end(), aa) == aod.end());
3426
3427 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3428 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
3429 BEAST_EXPECT(
3430 // NOLINTNEXTLINE(modernize-use-ranges)
3431 std::find(bod.begin(), bod.end(), bb) != bod.end());
3432 }
3433
3434 env.close(5s);
3435 env(escrow::cancel(bob, bob, bseq));
3436 {
3437 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
3438 BEAST_EXPECT(
3439 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3440
3441 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3442 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
3443 BEAST_EXPECT(
3444 // NOLINTNEXTLINE(modernize-use-ranges)
3445 std::find(bod.begin(), bod.end(), bb) == bod.end());
3446 }
3447 }
3448
3449 {
3450 testcase("MPT Metadata to other");
3451
3452 Env env{*this, features};
3453 MPTTester mptGw(env, gw, {.holders = {alice, bob, carol}});
3454 mptGw.create(
3455 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3456 mptGw.authorize({.account = alice});
3457 mptGw.authorize({.account = bob});
3458 mptGw.authorize({.account = carol});
3459 auto const mpt = mptGw["MPT"];
3460 env(pay(gw, alice, mpt(10'000)));
3461 env(pay(gw, bob, mpt(10'000)));
3462 env(pay(gw, carol, mpt(10'000)));
3463 env.close();
3464 auto const aseq = env.seq(alice);
3465 auto const bseq = env.seq(bob);
3466
3467 env(escrow::create(alice, bob, mpt(1'000)), escrow::kFinishTime(env.now() + 1s));
3468 BEAST_EXPECT(
3469 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3470 env.close(5s);
3471 env(escrow::create(bob, carol, mpt(1'000)),
3472 escrow::kFinishTime(env.now() + 1s),
3473 escrow::kCancelTime(env.now() + 2s));
3474 BEAST_EXPECT(
3475 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3476 env.close(5s);
3477
3478 auto const ab = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
3479 BEAST_EXPECT(ab);
3480
3481 auto const bc = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
3482 BEAST_EXPECT(bc);
3483
3484 {
3485 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3486 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
3487 BEAST_EXPECT(
3488 // NOLINTNEXTLINE(modernize-use-ranges)
3489 std::find(aod.begin(), aod.end(), ab) != aod.end());
3490
3491 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3492 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 3);
3493 BEAST_EXPECT(
3494 // NOLINTNEXTLINE(modernize-use-ranges)
3495 std::find(bod.begin(), bod.end(), ab) != bod.end());
3496 BEAST_EXPECT(
3497 // NOLINTNEXTLINE(modernize-use-ranges)
3498 std::find(bod.begin(), bod.end(), bc) != bod.end());
3499
3500 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
3501 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
3502 BEAST_EXPECT(
3503 // NOLINTNEXTLINE(modernize-use-ranges)
3504 std::find(cod.begin(), cod.end(), bc) != cod.end());
3505 }
3506
3507 env.close(5s);
3508 env(escrow::finish(alice, alice, aseq));
3509 {
3510 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
3511 BEAST_EXPECT(env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
3512
3513 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3514 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
3515 BEAST_EXPECT(
3516 // NOLINTNEXTLINE(modernize-use-ranges)
3517 std::find(aod.begin(), aod.end(), ab) == aod.end());
3518
3519 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3520 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
3521 BEAST_EXPECT(
3522 // NOLINTNEXTLINE(modernize-use-ranges)
3523 std::find(bod.begin(), bod.end(), ab) == bod.end());
3524 BEAST_EXPECT(
3525 // NOLINTNEXTLINE(modernize-use-ranges)
3526 std::find(bod.begin(), bod.end(), bc) != bod.end());
3527
3528 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
3529 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
3530 }
3531
3532 env.close(5s);
3533 env(escrow::cancel(bob, bob, bseq));
3534 {
3535 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
3536 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
3537
3538 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3539 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
3540 BEAST_EXPECT(
3541 // NOLINTNEXTLINE(modernize-use-ranges)
3542 std::find(aod.begin(), aod.end(), ab) == aod.end());
3543
3544 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3545 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
3546 BEAST_EXPECT(
3547 // NOLINTNEXTLINE(modernize-use-ranges)
3548 std::find(bod.begin(), bod.end(), ab) == bod.end());
3549 BEAST_EXPECT(
3550 // NOLINTNEXTLINE(modernize-use-ranges)
3551 std::find(bod.begin(), bod.end(), bc) == bod.end());
3552
3553 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
3554 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
3555 }
3556 }
3557 }
3558
3559 void
3561 {
3562 testcase("MPT Gateway Balances");
3563 using namespace test::jtx;
3564 using namespace std::literals;
3565
3566 // issuer is dest; alice w/ authorization
3567 {
3568 Env env{*this, features};
3569 auto const baseFee = env.current()->fees().base;
3570 auto const alice = Account("alice");
3571 auto const gw = Account("gw");
3572
3573 MPTTester mptGw(env, gw, {.holders = {alice}});
3574 mptGw.create(
3575 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3576 mptGw.authorize({.account = alice});
3577 auto const mpt = mptGw["MPT"];
3578 env(pay(gw, alice, mpt(10'000)));
3579 env.close();
3580
3581 // issuer can be destination
3582 auto const seq1 = env.seq(alice);
3583 auto const preAliceMPT = env.balance(alice, mpt);
3584 auto const preOutstanding = env.balance(gw, mpt);
3585 auto const preEscrowed = issuerMPTEscrowed(env, mpt);
3586 BEAST_EXPECT(preOutstanding == mpt(-10'000));
3587 BEAST_EXPECT(preEscrowed == 0);
3588
3589 env(escrow::create(alice, gw, mpt(1'000)),
3591 escrow::kFinishTime(env.now() + 1s),
3592 Fee(baseFee * 150));
3593 env.close();
3594
3595 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3596 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3597 BEAST_EXPECT(env.balance(gw, mpt) == preOutstanding);
3598 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == preEscrowed + 1'000);
3599
3600 // issuer (dest) can finish escrow
3601 env(escrow::finish(gw, alice, seq1),
3604 Fee(baseFee * 150));
3605 env.close();
3606
3607 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3608 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3609 BEAST_EXPECT(env.balance(gw, mpt) == preOutstanding + mpt(1'000));
3610 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == preEscrowed);
3611 }
3612 }
3613
3614 void
3616 {
3617 testcase("MPT Locked Rate");
3618 using namespace test::jtx;
3619 using namespace std::literals;
3620
3621 auto const alice = Account("alice");
3622 auto const bob = Account("bob");
3623 auto const carol = Account("carol");
3624 auto const gw = Account{"gateway"};
3625 auto const usd = gw["USD"];
3626
3627 // test locked rate: finish
3628 {
3629 Env env{*this, features};
3630 auto const baseFee = env.current()->fees().base;
3631 auto const alice = Account("alice");
3632 auto const bob = Account("bob");
3633 auto const gw = Account("gw");
3634
3635 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3636 mptGw.create(
3637 {.transferFee = 25000,
3638 .ownerCount = 1,
3639 .holderCount = 0,
3640 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3641 mptGw.authorize({.account = alice});
3642 mptGw.authorize({.account = bob});
3643 auto const mpt = mptGw["MPT"];
3644 env(pay(gw, alice, mpt(10'000)));
3645 env(pay(gw, bob, mpt(10'000)));
3646 env.close();
3647
3648 // alice can create escrow w/ xfer rate
3649 auto const preAlice = env.balance(alice, mpt);
3650 auto const seq1 = env.seq(alice);
3651 auto const delta = mpt(125);
3652 env(escrow::create(alice, bob, mpt(125)),
3654 escrow::kFinishTime(env.now() + 1s),
3655 Fee(baseFee * 150));
3656 env.close();
3657 auto const transferRate = escrow::rate(env, alice, seq1);
3658 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
3659
3660 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 125);
3661 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 125);
3662 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-20'000));
3663
3664 // bob can finish escrow
3665 env(escrow::finish(bob, alice, seq1),
3668 Fee(baseFee * 150));
3669 env.close();
3670
3671 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - delta);
3672 BEAST_EXPECT(env.balance(bob, mpt) == mpt(10'100));
3673
3674 auto const escrowedWithFix = env.current()->rules().enabled(fixTokenEscrowV1) ? 0 : 25;
3675 auto const outstandingWithFix =
3676 env.current()->rules().enabled(fixTokenEscrowV1) ? mpt(19'975) : mpt(20'000);
3677 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowedWithFix);
3678 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowedWithFix);
3679 BEAST_EXPECT(env.balance(gw, mpt) == -outstandingWithFix);
3680 }
3681
3682 // test locked rate: cancel
3683 {
3684 Env env{*this, features};
3685 auto const baseFee = env.current()->fees().base;
3686 auto const alice = Account("alice");
3687 auto const bob = Account("bob");
3688 auto const gw = Account("gw");
3689
3690 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3691 mptGw.create(
3692 {.transferFee = 25000,
3693 .ownerCount = 1,
3694 .holderCount = 0,
3695 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3696 mptGw.authorize({.account = alice});
3697 mptGw.authorize({.account = bob});
3698 auto const mpt = mptGw["MPT"];
3699 env(pay(gw, alice, mpt(10'000)));
3700 env(pay(gw, bob, mpt(10'000)));
3701 env.close();
3702
3703 // alice can create escrow w/ xfer rate
3704 auto const preAlice = env.balance(alice, mpt);
3705 auto const preBob = env.balance(bob, mpt);
3706 auto const seq1 = env.seq(alice);
3707 auto const delta = mpt(125);
3708 env(escrow::create(alice, bob, mpt(125)),
3710 escrow::kFinishTime(env.now() + 1s),
3711 escrow::kCancelTime(env.now() + 3s),
3712 Fee(baseFee * 150));
3713 env.close();
3714 auto const transferRate = escrow::rate(env, alice, seq1);
3715 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
3716
3717 // alice can cancel escrow
3718 env(escrow::cancel(alice, alice, seq1), Fee(baseFee));
3719 env.close();
3720
3721 BEAST_EXPECT(env.balance(alice, mpt) == preAlice);
3722 BEAST_EXPECT(env.balance(bob, mpt) == preBob);
3723 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-20'000));
3724 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3725 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3726 }
3727
3728 // test locked rate: issuer is destination
3729 {
3730 Env env{*this, features};
3731 auto const baseFee = env.current()->fees().base;
3732 auto const alice = Account("alice");
3733 auto const bob = Account("bob");
3734 auto const gw = Account("gw");
3735
3736 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3737 mptGw.create(
3738 {.transferFee = 25000,
3739 .ownerCount = 1,
3740 .holderCount = 0,
3741 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3742 mptGw.authorize({.account = alice});
3743 mptGw.authorize({.account = bob});
3744 auto const mpt = mptGw["MPT"];
3745 env(pay(gw, alice, mpt(10'000)));
3746 env(pay(gw, bob, mpt(10'000)));
3747 env.close();
3748
3749 // alice can create escrow w/ xfer rate
3750 auto const preAlice = env.balance(alice, mpt);
3751 auto const seq1 = env.seq(alice);
3752 auto const delta = mpt(125);
3753 env(escrow::create(alice, gw, mpt(125)),
3755 escrow::kFinishTime(env.now() + 1s),
3756 Fee(baseFee * 150));
3757 env.close();
3758 auto const transferRate = escrow::rate(env, alice, seq1);
3759 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
3760
3761 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 125);
3762 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 125);
3763 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-20'000));
3764
3765 // bob can finish escrow
3766 env(escrow::finish(gw, alice, seq1),
3769 Fee(baseFee * 150));
3770 env.close();
3771
3772 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - delta);
3773 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3774 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3775 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-19'875));
3776 }
3777 }
3778
3779 void
3781 {
3782 using namespace test::jtx;
3783 using namespace std::literals;
3784
3785 bool const withCleanup340 = features[fixCleanup3_4_0];
3786 testcase(
3787 std::string("MPT Split Escrow Transfer Fee ") +
3788 (withCleanup340 ? "with Cleanup340" : "without Cleanup340"));
3789
3790 Env env{*this, features};
3791 auto const baseFee = env.current()->fees().base;
3792 auto const alice = Account("alice");
3793 auto const bob = Account("bob");
3794 auto const gw = Account("gw");
3795 env.fund(XRP(1'000), alice, bob, gw);
3796 env.close();
3797
3798 MPTTester const mpt({
3799 .env = env,
3800 .issuer = gw,
3801 .holders = {alice, bob},
3802 .transferFee = 1'000,
3803 .flags = tfMPTCanEscrow | tfMPTCanTransfer,
3804 });
3805 env(pay(gw, alice, mpt(10'000)));
3806 env.close();
3807
3808 static constexpr int escrowCount = 10;
3809 static constexpr int splitAmount = 10;
3810 static constexpr int totalLocked = escrowCount * splitAmount;
3812 for (auto& seq : seqs)
3813 {
3814 seq = env.seq(alice);
3815 env(escrow::create(alice, bob, mpt(splitAmount)),
3817 escrow::kFinishTime(env.now() + 1s),
3818 Fee(baseFee * 150));
3819 env.close();
3820 }
3821
3822 BEAST_EXPECT(env.balance(alice, mpt) == mpt(10'000 - totalLocked));
3823 BEAST_EXPECT(env.balance(bob, mpt) == mpt(0));
3824 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-10'000));
3825 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == totalLocked);
3826 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == totalLocked);
3827
3828 for (auto const seq : seqs)
3829 {
3830 env(escrow::finish(bob, alice, seq),
3833 Fee(baseFee * 150));
3834 env.close();
3835 }
3836
3837 auto const feeBurned = withCleanup340 ? escrowCount : 0;
3838 BEAST_EXPECT(env.balance(alice, mpt) == mpt(10'000 - totalLocked));
3839 BEAST_EXPECT(env.balance(bob, mpt) == mpt(totalLocked - feeBurned));
3840 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-10'000 + feeBurned));
3841 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3842 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3843 }
3844
3845 void
3847 {
3848 testcase("MPT large locked rate");
3849 using namespace test::jtx;
3850 using namespace std::literals;
3851
3852 auto constexpr escrowAmount = 200'000'000'000'000'000LL;
3853 auto constexpr noOverflowEscrowAmount = 186'000'000'000'000'000LL;
3854 auto const alice = Account("alice");
3855 auto const bob = Account("bob");
3856 auto const gw = Account("gw");
3857
3858 for (auto const testFeatures :
3859 {features - featureMPTokensV2 - fixCleanup3_4_0,
3860 features - featureMPTokensV2,
3861 (features | featureMPTokensV2) - fixCleanup3_4_0,
3862 features | featureMPTokensV2})
3863 {
3864 bool const mptV2 = testFeatures[featureMPTokensV2];
3865 bool const tokenEscrowV1 = testFeatures[fixTokenEscrowV1];
3866 // The transfer-fee split in EscrowFinish only overflows on the
3867 // legacy divideRound(amount, lockedRate, ...) path, which runs when
3868 // fixCleanup3_4_0 is disabled. With fixCleanup3_4_0 the split uses
3869 // mulRatio (128-bit intermediate), which cannot overflow. Without
3870 // it, this large amount overflows unless the MPTokensV2 Number path
3871 // is active. So the finish succeeds when either amendment is enabled.
3872 bool const cleanup340 = testFeatures[fixCleanup3_4_0];
3873 bool const noOverflow = cleanup340 || mptV2;
3874 auto const expectedErr = noOverflow ? Ter(tesSUCCESS) : Ter(tefEXCEPTION);
3875
3876 // Finish with a large MPT amount and non-zero transfer fee. When the
3877 // computation overflows (legacy divideRound path, no MPTokensV2) the
3878 // finish fails with tefEXCEPTION and the escrow is untouched;
3879 // otherwise it unlocks the escrow.
3880 {
3881 Env env{*this, testFeatures};
3882 env.fund(XRP(1'000), alice, bob, gw);
3883 auto const baseFee = env.current()->fees().base;
3884
3885 MPTTester const mpt(
3886 {.env = env,
3887 .issuer = gw,
3888 .holders = {alice, bob},
3889 .transferFee = 1'000,
3890 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3891 env(pay(gw, alice, mpt(escrowAmount)));
3892 env.close();
3893
3894 auto const preAlice = env.balance(alice, mpt);
3895 auto const preBob = env.balance(bob, mpt);
3896 auto const seq = env.seq(alice);
3897 env(escrow::create(alice, bob, mpt(escrowAmount)),
3899 escrow::kFinishTime(env.now() + 1s),
3900 escrow::kCancelTime(env.now() + 500s),
3901 Fee(baseFee * 150));
3902 env.close();
3903
3904 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowAmount);
3905 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowAmount);
3906
3907 env(escrow::finish(bob, alice, seq),
3910 Fee(baseFee * 150),
3911 expectedErr);
3912 env.close();
3913
3914 if (noOverflow)
3915 {
3916 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
3917 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - mpt(escrowAmount));
3918 auto const postBob = env.balance(bob, mpt);
3919 BEAST_EXPECT(postBob.value() > preBob.value());
3920 BEAST_EXPECT(postBob.value() < (preBob + mpt(escrowAmount)).value());
3921 auto const xferFee = escrowAmount - (postBob.value() - preBob.value());
3922 auto const expectedEscrow = tokenEscrowV1 ? 0 : xferFee;
3923 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == expectedEscrow);
3924 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == expectedEscrow);
3925 }
3926 else
3927 {
3928 BEAST_EXPECT(env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
3929 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - mpt(escrowAmount));
3930 BEAST_EXPECT(env.balance(bob, mpt) == preBob);
3931 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowAmount);
3932 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowAmount);
3933 }
3934 }
3935
3936 // Control: a still-large amount below the legacy overflow boundary
3937 // finishes successfully in both feature modes.
3938 {
3939 Env env{*this, testFeatures};
3940 env.fund(XRP(1'000), alice, bob, gw);
3941 auto const baseFee = env.current()->fees().base;
3942
3943 MPTTester const mpt(
3944 {.env = env,
3945 .issuer = gw,
3946 .holders = {alice, bob},
3947 .transferFee = 1'000,
3948 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3949 env(pay(gw, alice, mpt(noOverflowEscrowAmount)));
3950 env.close();
3951
3952 auto const preAlice = env.balance(alice, mpt);
3953 auto const preBob = env.balance(bob, mpt);
3954 auto const seq = env.seq(alice);
3955 env(escrow::create(alice, bob, mpt(noOverflowEscrowAmount)),
3957 escrow::kFinishTime(env.now() + 1s),
3958 escrow::kCancelTime(env.now() + 500s),
3959 Fee(baseFee * 150));
3960 env.close();
3961
3962 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == noOverflowEscrowAmount);
3963 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == noOverflowEscrowAmount);
3964
3965 env(escrow::finish(bob, alice, seq),
3968 Fee(baseFee * 150),
3969 Ter(tesSUCCESS));
3970 env.close();
3971
3972 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
3973 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - mpt(noOverflowEscrowAmount));
3974 auto const postBob = env.balance(bob, mpt);
3975 BEAST_EXPECT(postBob.value() > preBob.value());
3976 BEAST_EXPECT(postBob.value() < (preBob + mpt(noOverflowEscrowAmount)).value());
3977 auto const xferFee = noOverflowEscrowAmount - (postBob.value() - preBob.value());
3978 auto const expectedEscrow = tokenEscrowV1 ? 0 : xferFee;
3979 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == expectedEscrow);
3980 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == expectedEscrow);
3981 }
3982
3983 // Cancel returns the escrow to the owner using parity rate, so it
3984 // does not hit the transfer-rate division in either feature mode.
3985 {
3986 Env env{*this, testFeatures};
3987 env.fund(XRP(1'000), alice, bob, gw);
3988 auto const baseFee = env.current()->fees().base;
3989
3990 MPTTester const mpt(
3991 {.env = env,
3992 .issuer = gw,
3993 .holders = {alice, bob},
3994 .transferFee = 1'000,
3995 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3996 env(pay(gw, alice, mpt(escrowAmount)));
3997 env.close();
3998
3999 auto const preAlice = env.balance(alice, mpt);
4000 auto const preBob = env.balance(bob, mpt);
4001 auto const seq = env.seq(alice);
4002 env(escrow::create(alice, bob, mpt(escrowAmount)),
4004 escrow::kFinishTime(env.now() + 1s),
4005 escrow::kCancelTime(env.now() + 3s),
4006 Fee(baseFee * 150));
4007 env.close();
4008
4009 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowAmount);
4010 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowAmount);
4011
4012 env(escrow::cancel(alice, alice, seq), Fee(baseFee), Ter(tesSUCCESS));
4013 env.close();
4014
4015 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
4016 BEAST_EXPECT(env.balance(alice, mpt) == preAlice);
4017 BEAST_EXPECT(env.balance(bob, mpt) == preBob);
4018 BEAST_EXPECT(env.balance(gw, mpt) == -mpt(escrowAmount));
4019 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
4020 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
4021 }
4022 }
4023 }
4024
4025 void
4027 {
4028 testcase("MPT Require Auth");
4029 using namespace test::jtx;
4030 using namespace std::literals;
4031
4032 Env env{*this, features};
4033 auto const baseFee = env.current()->fees().base;
4034 auto const alice = Account("alice");
4035 auto const bob = Account("bob");
4036 auto const gw = Account("gw");
4037
4038 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
4039 mptGw.create(
4040 {.ownerCount = 1,
4041 .holderCount = 0,
4042 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
4043 mptGw.authorize({.account = alice});
4044 mptGw.authorize({.account = gw, .holder = alice});
4045 mptGw.authorize({.account = bob});
4046 mptGw.authorize({.account = gw, .holder = bob});
4047 auto const mpt = mptGw["MPT"];
4048 env(pay(gw, alice, mpt(10'000)));
4049 env.close();
4050
4051 auto seq = env.seq(alice);
4052 auto const delta = mpt(125);
4053 // alice can create escrow - is authorized
4054 env(escrow::create(alice, bob, mpt(100)),
4056 escrow::kFinishTime(env.now() + 1s),
4057 Fee(baseFee * 150));
4058 env.close();
4059
4060 // bob can finish escrow - is authorized
4061 env(escrow::finish(bob, alice, seq),
4064 Fee(baseFee * 150));
4065 env.close();
4066 }
4067
4068 void
4070 {
4071 testcase("MPT Lock");
4072 using namespace test::jtx;
4073 using namespace std::literals;
4074
4075 Env env{*this, features};
4076 auto const baseFee = env.current()->fees().base;
4077 auto const alice = Account("alice");
4078 auto const bob = Account("bob");
4079 auto const gw = Account("gw");
4080
4081 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
4082 mptGw.create(
4083 {.ownerCount = 1,
4084 .holderCount = 0,
4085 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
4086 mptGw.authorize({.account = alice});
4087 mptGw.authorize({.account = bob});
4088 auto const mpt = mptGw["MPT"];
4089 env(pay(gw, alice, mpt(10'000)));
4090 env(pay(gw, bob, mpt(10'000)));
4091 env.close();
4092
4093 // alice create escrow
4094 auto seq1 = env.seq(alice);
4095 env(escrow::create(alice, bob, mpt(100)),
4097 escrow::kFinishTime(env.now() + 1s),
4098 escrow::kCancelTime(env.now() + 2s),
4099 Fee(baseFee * 150));
4100 env.close();
4101
4102 // lock account & dest
4103 mptGw.set({.account = gw, .holder = alice, .flags = tfMPTLock});
4104 mptGw.set({.account = gw, .holder = bob, .flags = tfMPTLock});
4105
4106 // bob cannot finish
4107 env(escrow::finish(bob, alice, seq1),
4110 Fee(baseFee * 150),
4111 Ter(tecLOCKED));
4112 env.close();
4113
4114 // bob can cancel
4115 env(escrow::cancel(bob, alice, seq1));
4116 env.close();
4117 }
4118
4119 void
4121 {
4122 testcase("MPT Can Transfer");
4123 using namespace test::jtx;
4124 using namespace std::literals;
4125
4126 Env env{*this, features};
4127 auto const baseFee = env.current()->fees().base;
4128 auto const alice = Account("alice");
4129 auto const bob = Account("bob");
4130 auto const gw = Account("gw");
4131
4132 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
4133 mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow});
4134 mptGw.authorize({.account = alice});
4135 mptGw.authorize({.account = bob});
4136 auto const mpt = mptGw["MPT"];
4137 env(pay(gw, alice, mpt(10'000)));
4138 env(pay(gw, bob, mpt(10'000)));
4139 env.close();
4140
4141 // alice cannot create escrow to non issuer
4142 env(escrow::create(alice, bob, mpt(100)),
4144 escrow::kFinishTime(env.now() + 1s),
4145 escrow::kCancelTime(env.now() + 2s),
4146 Fee(baseFee * 150),
4147 Ter(tecNO_AUTH));
4148 env.close();
4149
4150 // Escrow Create & Finish
4151 {
4152 // alice an create escrow to issuer
4153 auto seq = env.seq(alice);
4154 env(escrow::create(alice, gw, mpt(100)),
4156 escrow::kFinishTime(env.now() + 1s),
4157 Fee(baseFee * 150));
4158 env.close();
4159
4160 // gw can finish
4161 env(escrow::finish(gw, alice, seq),
4164 Fee(baseFee * 150));
4165 env.close();
4166 }
4167
4168 // Escrow Create & Cancel
4169 {
4170 // alice an create escrow to issuer
4171 auto seq = env.seq(alice);
4172 env(escrow::create(alice, gw, mpt(100)),
4174 escrow::kFinishTime(env.now() + 1s),
4175 escrow::kCancelTime(env.now() + 2s),
4176 Fee(baseFee * 150));
4177 env.close();
4178
4179 // alice can cancel
4180 env(escrow::cancel(alice, alice, seq));
4181 env.close();
4182 }
4183 }
4184
4185 void
4187 {
4188 testcase("MPT Destroy");
4189 using namespace test::jtx;
4190 using namespace std::literals;
4191
4192 // tecHAS_OBLIGATIONS: issuer cannot destroy issuance
4193 {
4194 Env env{*this, features};
4195 auto const baseFee = env.current()->fees().base;
4196 auto const alice = Account("alice");
4197 auto const bob = Account("bob");
4198 auto const gw = Account("gw");
4199
4200 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
4201 mptGw.create(
4202 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
4203 mptGw.authorize({.account = alice});
4204 mptGw.authorize({.account = bob});
4205 auto const mpt = mptGw["MPT"];
4206 env(pay(gw, alice, mpt(10'000)));
4207 env(pay(gw, bob, mpt(10'000)));
4208 env.close();
4209
4210 auto const seq1 = env.seq(alice);
4211 env(escrow::create(alice, bob, mpt(10)),
4213 escrow::kFinishTime(env.now() + 1s),
4214 Fee(baseFee * 150));
4215 env.close();
4216
4217 env(pay(alice, gw, mpt(10'000)), Ter(tecPATH_PARTIAL));
4218 env(pay(alice, gw, mpt(9'990)));
4219 env(pay(bob, gw, mpt(10'000)));
4220 BEAST_EXPECT(env.balance(alice, mpt) == mpt(0));
4221 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 10);
4222 BEAST_EXPECT(env.balance(bob, mpt) == mpt(0));
4223 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
4224 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-10));
4225 mptGw.authorize({.account = bob, .flags = tfMPTUnauthorize});
4226 mptGw.destroy({.id = mptGw.issuanceID(), .ownerCount = 1, .err = tecHAS_OBLIGATIONS});
4227
4228 env(escrow::finish(bob, alice, seq1),
4231 Fee(baseFee * 150),
4232 Ter(tesSUCCESS));
4233 env.close();
4234
4235 env(pay(bob, gw, mpt(10)));
4236 mptGw.destroy({.id = mptGw.issuanceID(), .ownerCount = 0});
4237 }
4238
4239 // tecHAS_OBLIGATIONS: holder cannot destroy mptoken
4240 {
4241 Env env{*this, features};
4242 auto const baseFee = env.current()->fees().base;
4243 auto const alice = Account("alice");
4244 auto const bob = Account("bob");
4245 auto const gw = Account("gw");
4246 env.fund(XRP(10'000), bob);
4247 env.close();
4248
4249 MPTTester mptGw(env, gw, {.holders = {alice}});
4250 mptGw.create(
4251 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
4252 mptGw.authorize({.account = alice});
4253 auto const mpt = mptGw["MPT"];
4254 env(pay(gw, alice, mpt(10'000)));
4255 env.close();
4256
4257 auto const seq1 = env.seq(alice);
4258 env(escrow::create(alice, bob, mpt(10)),
4260 escrow::kFinishTime(env.now() + 1s),
4261 Fee(baseFee * 150),
4262 Ter(tesSUCCESS));
4263 env.close();
4264
4265 env(pay(alice, gw, mpt(9'990)));
4266 env.close();
4267
4268 BEAST_EXPECT(env.balance(alice, mpt) == mpt(0));
4269 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 10);
4270 mptGw.authorize(
4271 {.account = alice, .flags = tfMPTUnauthorize, .err = tecHAS_OBLIGATIONS});
4272
4273 env(escrow::finish(bob, alice, seq1),
4276 Fee(baseFee * 150),
4277 Ter(tesSUCCESS));
4278 env.close();
4279
4280 BEAST_EXPECT(env.balance(alice, mpt) == mpt(0));
4281 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
4282 mptGw.authorize({.account = alice, .flags = tfMPTUnauthorize});
4283 BEAST_EXPECT(!env.le(keylet::mptoken(mpt.mpt(), alice)));
4284 }
4285 }
4286
4287 void
4289 {
4290 testIOUEnablement(features);
4291 testIOUAllowLockingFlag(features);
4292 testIOUCreatePreflight(features);
4293 testIOUCreatePreclaim(features);
4294 testIOUFinishPreclaim(features);
4295 testIOUFinishDoApply(features);
4296 testIOUCancelPreclaim(features);
4297 testIOUCancelDoApply(features);
4298 testIOUBalances(features);
4299 testIOUMetaAndOwnership(features);
4300 testIOURippleState(features);
4301 testIOUGateway(features);
4302 testIOULockedRate(features);
4303 testIOULimitAmount(features);
4304 testIOURequireAuth(features);
4305 testIOUFreeze(features);
4306 testIOUInsufficientFunds(features);
4307 testIOUPrecisionLoss(features);
4308 }
4309
4310 void
4312 {
4313 testMPTEnablement(features);
4314 testMPTCreatePreflight(features);
4315 testMPTCreatePreclaim(features);
4316 testMPTFinishPreclaim(features);
4317 testMPTFinishDoApply(features);
4318 testMPTCancelPreclaim(features);
4319 testMPTBalances(features);
4320 testMPTMetaAndOwnership(features);
4321 testMPTGateway(features);
4322 testMPTLockedRate(features);
4323 testMPTLargeLockedRate(features);
4324 testMPTRequireAuth(features);
4325 testMPTLock(features);
4326 testMPTCanTransfer(features);
4327 testMPTDestroy(features);
4328 }
4329
4330public:
4331 void
4332 run() override
4333 {
4334 using namespace test::jtx;
4335 FeatureBitset const all{testableAmendments()};
4336 for (FeatureBitset const& feats :
4337 {all - featureSingleAssetVault - featureLendingProtocol - featureMPTokensV2, all})
4338 {
4339 testIOUWithFeats(feats);
4340 testIOUWithFeats(feats - fixCleanup3_2_0);
4341 testMPTWithFeats(feats);
4342 testMPTWithFeats(feats - fixTokenEscrowV1);
4343 }
4344 testMPTSplitEscrowTransferFee(all - fixCleanup3_4_0);
4346 testIOUCancelReserveRecycle(all - fixCleanup3_4_0);
4348 }
4349};
4350
4351BEAST_DEFINE_TESTSUITE(EscrowToken, app, xrpl);
4352
4353} // namespace xrpl::test
A generic endpoint for log messages.
Definition Journal.h:44
A testsuite class.
Definition suite.h:52
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
Value removeMember(char const *key)
Remove and return the named member.
A class that simplifies iterating ledger directory pages.
Definition Dir.h:30
ConstIterator begin() const
Definition Dir.cpp:26
ConstIterator end() const
Definition Dir.cpp:44
A currency issued by an account.
Definition Issue.h:18
Currency currency
Definition Issue.h:20
bool modify(ModifyType const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:59
XRPAmount xrp() const
Definition STAmount.cpp:272
Discardable, editable view to a ledger.
Definition Sandbox.h:18
void apply(RawView &to)
Definition Sandbox.h:38
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:62
virtual OpenLedger & getOpenLedger()=0
constexpr value_type drops() const
Returns the number of drops.
Definition XRPAmount.h:170
void insert(SLE::Ref sle) override
Insert a new state SLE.
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
A transaction testing environment.
Definition Env.h:161
Application & app()
Definition Env.h:300
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:311
std::uint32_t ownerCount(Account const &account) const
Return the number of objects owned by an account.
Definition Env.cpp:266
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
PrettyAmount limit(Account const &account, Issue const &issue) const
Returns the IOU limit on an account.
Definition Env.cpp:254
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:302
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:1058
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:354
std::shared_ptr< STObject const > meta()
Return metadata for the last JTx.
Definition Env.cpp:538
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:174
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:766
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:377
NetClock::time_point now()
Returns the current network time.
Definition Env.h:326
Set the fee on a JTx.
Definition fee.h:20
Converts to IOU Issue or STAmount.
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
Definition mpt.h:512
void create(MPTCreate const &arg=MPTCreate{}, std::source_location const &loc=std::source_location::current())
Definition mpt.cpp:343
Converts to MPT Issue or STAmount.
xrpl::MPTID const & mpt() const
Match clear account flags.
Definition flags.h:137
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
Set the flags on a JTx.
Definition txflags.h:14
T distance(T... args)
T find(T... args)
T make_shared(T... args)
Keylet escrow(AccountID const &src, SeqProxy const &seq) noexcept
An escrow entry.
Definition Indexes.cpp:418
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:403
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:573
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:567
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:275
json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
Definition escrow.cpp:24
json::Value cancel(AccountID const &account, Account const &from, std::uint32_t seq)
Definition escrow.cpp:48
auto const kCondition
Definition escrow.h:85
auto const kCancelTime
Set the "CancelAfter" time tag on a JTx.
Definition escrow.h:83
Rate rate(Env &env, Account const &account, std::uint32_t const &seq)
Definition escrow.cpp:60
auto const kFinishTime
Set the "FinishAfter" time tag on a JTx.
Definition escrow.h:78
std::array< std::uint8_t, 39 > const kCb2
Definition escrow.h:62
auto const kFulfillment
Definition escrow.h:87
json::Value finish(AccountID const &account, AccountID const &from, std::uint32_t seq)
Definition escrow.cpp:36
std::array< std::uint8_t, 4 > const kFb1
Definition escrow.h:52
std::array< std::uint8_t, 39 > const kCb1
Definition escrow.h:54
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:110
FeatureBitset testableAmendments()
Definition Env.h:92
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:18
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.
Definition rate.cpp:15
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:15
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ tefINTERNAL
Definition TER.h:168
@ tefEXCEPTION
Definition TER.h:167
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:908
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
MPTID makeMptID(std::uint32_t const sequence, AccountID const &account)
Definition Indexes.cpp:206
@ TapNone
Definition ApplyView.h:28
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
@ temBAD_CURRENCY
Definition TER.h:78
@ temBAD_FEE
Definition TER.h:80
@ temDISABLED
Definition TER.h:102
@ temBAD_AMOUNT
Definition TER.h:77
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecLOCKED
Definition TER.h:366
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:300
@ tecPATH_PARTIAL
Definition TER.h:290
@ tecNO_TARGET
Definition TER.h:312
@ tecOBJECT_NOT_FOUND
Definition TER.h:334
@ tecNO_AUTH
Definition TER.h:308
@ tecFROZEN
Definition TER.h:311
@ tecINSUFFICIENT_FUNDS
Definition TER.h:333
@ tecNO_LINE
Definition TER.h:309
@ tecPRECISION_LOSS
Definition TER.h:371
@ tecINSUFFICIENT_RESERVE
Definition TER.h:315
@ tecLIMIT_EXCEEDED
Definition TER.h:369
@ tecNO_PERMISSION
Definition TER.h:313
@ tecNO_ISSUER
Definition TER.h:307
@ tecHAS_OBLIGATIONS
Definition TER.h:325
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:297
@ tesSUCCESS
Definition TER.h:250
void testMPTLargeLockedRate(FeatureBitset features)
void testMPTCreatePreclaim(FeatureBitset features)
void testMPTFinishDoApply(FeatureBitset features)
void testMPTMetaAndOwnership(FeatureBitset features)
void testIOUBalances(FeatureBitset features)
void testMPTSplitEscrowTransferFee(FeatureBitset features)
void testIOUCreatePreclaim(FeatureBitset features)
void testMPTGateway(FeatureBitset features)
void testMPTCanTransfer(FeatureBitset features)
void testIOUFinishPreclaim(FeatureBitset features)
static jtx::PrettyAmount issuerEscrowed(jtx::Env &env, jtx::Account const &account, Issue const &issue)
void run() override
Runs the suite.
void testIOUMetaAndOwnership(FeatureBitset features)
void testIOUPrecisionLoss(FeatureBitset features)
void testMPTLock(FeatureBitset features)
void testMPTCancelPreclaim(FeatureBitset features)
void testIOUEnablement(FeatureBitset features)
void testMPTBalances(FeatureBitset features)
static jtx::PrettyAmount issuerBalance(jtx::Env &env, jtx::Account const &account, Issue const &issue)
void testMPTRequireAuth(FeatureBitset features)
void testIOUFinishDoApply(FeatureBitset features)
void testIOUFreeze(FeatureBitset features)
void testMPTEnablement(FeatureBitset features)
static uint64_t mptEscrowed(jtx::Env const &env, jtx::Account const &account, jtx::MPT const &mpt)
void testIOUCancelPreclaim(FeatureBitset features)
void testIOUGateway(FeatureBitset features)
static uint64_t issuerMPTEscrowed(jtx::Env const &env, jtx::MPT const &mpt)
void testIOULimitAmount(FeatureBitset features)
void testIOUAllowLockingFlag(FeatureBitset features)
void testMPTCreatePreflight(FeatureBitset features)
void testIOULockedRate(FeatureBitset features)
void testMPTFinishPreclaim(FeatureBitset features)
void testIOUCancelDoApply(FeatureBitset features)
void testIOUInsufficientFunds(FeatureBitset features)
void testIOUWithFeats(FeatureBitset features)
void testIOUCancelReserveRecycle(FeatureBitset features)
void testMPTLockedRate(FeatureBitset features)
void testMPTWithFeats(FeatureBitset features)
void testIOUCreatePreflight(FeatureBitset features)
void testIOURequireAuth(FeatureBitset features)
void testMPTDestroy(FeatureBitset features)
void testIOURippleState(FeatureBitset features)
Represents an XRP, IOU, or MPT quantity This customizes the string conversion and supports XRP conver...
STAmount const & value() const