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 =
577 features[featureSingleAssetVault] || features[featureLendingProtocol];
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 Balances");
958
959 using namespace jtx;
960 using namespace std::chrono;
961
962 Env env{*this, features};
963 auto const baseFee = env.current()->fees().base;
964 auto const alice = Account("alice");
965 auto const bob = Account("bob");
966 auto const gw = Account{"gateway"};
967 auto const usd = gw["USD"];
968 env.fund(XRP(5000), alice, bob, gw);
969 env(fset(gw, asfAllowTrustLineLocking));
970 env.close();
971 env.trust(usd(10'000), alice, bob);
972 env.close();
973 env(pay(gw, alice, usd(5'000)));
974 env(pay(gw, bob, usd(5'000)));
975 env.close();
976
977 auto const outstandingUSD = usd(10'000);
978
979 // Create & Finish Escrow
980 auto const seq1 = env.seq(alice);
981 {
982 auto const preAliceUSD = env.balance(alice, usd);
983 auto const preBobUSD = env.balance(bob, usd);
984 env(escrow::create(alice, bob, usd(1'000)),
986 escrow::kFinishTime(env.now() + 1s),
987 Fee(baseFee * 150),
988 Ter(tesSUCCESS));
989 env.close();
990
991 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD - usd(1'000));
992 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD);
993 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD - usd(1'000));
994 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(1'000));
995 }
996 {
997 auto const preAliceUSD = env.balance(alice, usd);
998 auto const preBobUSD = env.balance(bob, usd);
999 env(escrow::finish(bob, alice, seq1),
1002 Fee(baseFee * 150),
1003 Ter(tesSUCCESS));
1004 env.close();
1005
1006 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD);
1007 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD + usd(1'000));
1008 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD);
1009 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(0));
1010 }
1011
1012 // Create & Cancel Escrow
1013 auto const seq2 = env.seq(alice);
1014 {
1015 auto const preAliceUSD = env.balance(alice, usd);
1016 auto const preBobUSD = env.balance(bob, usd);
1017 env(escrow::create(alice, bob, usd(1'000)),
1019 escrow::kFinishTime(env.now() + 1s),
1020 escrow::kCancelTime(env.now() + 2s),
1021 Fee(baseFee * 150),
1022 Ter(tesSUCCESS));
1023 env.close();
1024
1025 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD - usd(1'000));
1026 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD);
1027 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD - usd(1'000));
1028 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(1'000));
1029 }
1030 {
1031 auto const preAliceUSD = env.balance(alice, usd);
1032 auto const preBobUSD = env.balance(bob, usd);
1033 env(escrow::cancel(bob, alice, seq2), Ter(tesSUCCESS));
1034 env.close();
1035
1036 BEAST_EXPECT(env.balance(alice, usd) == preAliceUSD + usd(1'000));
1037 BEAST_EXPECT(env.balance(bob, usd) == preBobUSD);
1038 BEAST_EXPECT(issuerBalance(env, gw, usd) == outstandingUSD);
1039 BEAST_EXPECT(issuerEscrowed(env, gw, usd) == usd(0));
1040 }
1041 }
1042
1043 void
1045 {
1046 using namespace jtx;
1047 using namespace std::chrono;
1048
1049 auto const alice = Account("alice");
1050 auto const bob = Account("bob");
1051 auto const carol = Account("carol");
1052 auto const gw = Account{"gateway"};
1053 auto const usd = gw["USD"];
1054 {
1055 testcase("IOU Metadata to self");
1056
1057 Env env{*this, features};
1058 env.fund(XRP(5000), alice, bob, carol, gw);
1059 env(fset(gw, asfAllowTrustLineLocking));
1060 env.close();
1061 env.trust(usd(10'000), alice, bob, carol);
1062 env.close();
1063 env(pay(gw, alice, usd(5000)));
1064 env(pay(gw, bob, usd(5000)));
1065 env(pay(gw, carol, usd(5000)));
1066 env.close();
1067 auto const aseq = env.seq(alice);
1068 auto const bseq = env.seq(bob);
1069
1070 env(escrow::create(alice, alice, usd(1'000)),
1071 escrow::kFinishTime(env.now() + 1s),
1072 escrow::kCancelTime(env.now() + 500s));
1073 BEAST_EXPECT(
1074 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1075 env.close(5s);
1076 auto const aa = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
1077 BEAST_EXPECT(aa);
1078 {
1079 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1080 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
1081 BEAST_EXPECT(
1082 // NOLINTNEXTLINE(modernize-use-ranges)
1083 std::find(aod.begin(), aod.end(), aa) != aod.end());
1084 }
1085
1086 {
1087 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1088 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4);
1089 BEAST_EXPECT(
1090 // NOLINTNEXTLINE(modernize-use-ranges)
1091 std::find(iod.begin(), iod.end(), aa) != iod.end());
1092 }
1093
1094 env(escrow::create(bob, bob, usd(1'000)),
1095 escrow::kFinishTime(env.now() + 1s),
1096 escrow::kCancelTime(env.now() + 2s));
1097 BEAST_EXPECT(
1098 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1099 env.close(5s);
1100 auto const bb = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
1101 BEAST_EXPECT(bb);
1102
1103 {
1104 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1105 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1106 BEAST_EXPECT(
1107 // NOLINTNEXTLINE(modernize-use-ranges)
1108 std::find(bod.begin(), bod.end(), bb) != bod.end());
1109 }
1110
1111 {
1112 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1113 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 5);
1114 BEAST_EXPECT(
1115 // NOLINTNEXTLINE(modernize-use-ranges)
1116 std::find(iod.begin(), iod.end(), bb) != iod.end());
1117 }
1118
1119 env.close(5s);
1120 env(escrow::finish(alice, alice, aseq));
1121 {
1122 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1123 BEAST_EXPECT(
1124 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1125
1126 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1127 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1128 BEAST_EXPECT(
1129 // NOLINTNEXTLINE(modernize-use-ranges)
1130 std::find(aod.begin(), aod.end(), aa) == aod.end());
1131
1132 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1133 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1134 BEAST_EXPECT(
1135 // NOLINTNEXTLINE(modernize-use-ranges)
1136 std::find(bod.begin(), bod.end(), bb) != bod.end());
1137
1138 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1139 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4);
1140 BEAST_EXPECT(
1141 // NOLINTNEXTLINE(modernize-use-ranges)
1142 std::find(iod.begin(), iod.end(), bb) != iod.end());
1143 }
1144
1145 env.close(5s);
1146 env(escrow::cancel(bob, bob, bseq));
1147 {
1148 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
1149 BEAST_EXPECT(
1150 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1151
1152 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1153 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1154 BEAST_EXPECT(
1155 // NOLINTNEXTLINE(modernize-use-ranges)
1156 std::find(bod.begin(), bod.end(), bb) == bod.end());
1157
1158 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1159 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3);
1160 BEAST_EXPECT(
1161 // NOLINTNEXTLINE(modernize-use-ranges)
1162 std::find(iod.begin(), iod.end(), bb) == iod.end());
1163 }
1164 }
1165 {
1166 testcase("IOU Metadata to other");
1167
1168 Env env{*this, features};
1169 env.fund(XRP(5000), alice, bob, carol, gw);
1170 env(fset(gw, asfAllowTrustLineLocking));
1171 env.close();
1172 env.trust(usd(10'000), alice, bob, carol);
1173 env.close();
1174 env(pay(gw, alice, usd(5000)));
1175 env(pay(gw, bob, usd(5000)));
1176 env(pay(gw, carol, usd(5000)));
1177 env.close();
1178 auto const aseq = env.seq(alice);
1179 auto const bseq = env.seq(bob);
1180
1181 env(escrow::create(alice, bob, usd(1'000)), escrow::kFinishTime(env.now() + 1s));
1182 BEAST_EXPECT(
1183 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1184 env.close(5s);
1185 env(escrow::create(bob, carol, usd(1'000)),
1186 escrow::kFinishTime(env.now() + 1s),
1187 escrow::kCancelTime(env.now() + 2s));
1188 BEAST_EXPECT(
1189 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1190 env.close(5s);
1191
1192 auto const ab = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
1193 BEAST_EXPECT(ab);
1194
1195 auto const bc = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
1196 BEAST_EXPECT(bc);
1197
1198 {
1199 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1200 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
1201 BEAST_EXPECT(
1202 // NOLINTNEXTLINE(modernize-use-ranges)
1203 std::find(aod.begin(), aod.end(), ab) != aod.end());
1204
1205 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1206 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 3);
1207 BEAST_EXPECT(
1208 // NOLINTNEXTLINE(modernize-use-ranges)
1209 std::find(bod.begin(), bod.end(), ab) != bod.end());
1210 BEAST_EXPECT(
1211 // NOLINTNEXTLINE(modernize-use-ranges)
1212 std::find(bod.begin(), bod.end(), bc) != bod.end());
1213
1214 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1215 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
1216 BEAST_EXPECT(
1217 // NOLINTNEXTLINE(modernize-use-ranges)
1218 std::find(cod.begin(), cod.end(), bc) != cod.end());
1219
1220 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1221 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 5);
1222 BEAST_EXPECT(
1223 // NOLINTNEXTLINE(modernize-use-ranges)
1224 std::find(iod.begin(), iod.end(), ab) != iod.end());
1225 BEAST_EXPECT(
1226 // NOLINTNEXTLINE(modernize-use-ranges)
1227 std::find(iod.begin(), iod.end(), bc) != iod.end());
1228 }
1229
1230 env.close(5s);
1231 env(escrow::finish(alice, alice, aseq));
1232 {
1233 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1234 BEAST_EXPECT(env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
1235
1236 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1237 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1238 BEAST_EXPECT(
1239 // NOLINTNEXTLINE(modernize-use-ranges)
1240 std::find(aod.begin(), aod.end(), ab) == aod.end());
1241
1242 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1243 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
1244 BEAST_EXPECT(
1245 // NOLINTNEXTLINE(modernize-use-ranges)
1246 std::find(bod.begin(), bod.end(), ab) == bod.end());
1247 BEAST_EXPECT(
1248 // NOLINTNEXTLINE(modernize-use-ranges)
1249 std::find(bod.begin(), bod.end(), bc) != bod.end());
1250
1251 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1252 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
1253
1254 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1255 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 4);
1256 BEAST_EXPECT(
1257 // NOLINTNEXTLINE(modernize-use-ranges)
1258 std::find(iod.begin(), iod.end(), ab) == iod.end());
1259 BEAST_EXPECT(
1260 // NOLINTNEXTLINE(modernize-use-ranges)
1261 std::find(iod.begin(), iod.end(), bc) != iod.end());
1262 }
1263
1264 env.close(5s);
1265 env(escrow::cancel(bob, bob, bseq));
1266 {
1267 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1268 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
1269
1270 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1271 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1272 BEAST_EXPECT(
1273 // NOLINTNEXTLINE(modernize-use-ranges)
1274 std::find(aod.begin(), aod.end(), ab) == aod.end());
1275
1276 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
1277 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
1278 BEAST_EXPECT(
1279 // NOLINTNEXTLINE(modernize-use-ranges)
1280 std::find(bod.begin(), bod.end(), ab) == bod.end());
1281 BEAST_EXPECT(
1282 // NOLINTNEXTLINE(modernize-use-ranges)
1283 std::find(bod.begin(), bod.end(), bc) == bod.end());
1284
1285 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1286 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1287
1288 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1289 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3);
1290 BEAST_EXPECT(
1291 // NOLINTNEXTLINE(modernize-use-ranges)
1292 std::find(iod.begin(), iod.end(), ab) == iod.end());
1293 BEAST_EXPECT(
1294 // NOLINTNEXTLINE(modernize-use-ranges)
1295 std::find(iod.begin(), iod.end(), bc) == iod.end());
1296 }
1297 }
1298
1299 {
1300 testcase("IOU Metadata to issuer");
1301
1302 Env env{*this, features};
1303 env.fund(XRP(5000), alice, carol, gw);
1304 env(fset(gw, asfAllowTrustLineLocking));
1305 env.close();
1306 env.trust(usd(10'000), alice, carol);
1307 env.close();
1308 env(pay(gw, alice, usd(5000)));
1309 env(pay(gw, carol, usd(5000)));
1310 env.close();
1311 auto const aseq = env.seq(alice);
1312
1313 env(escrow::create(alice, gw, usd(1'000)), escrow::kFinishTime(env.now() + 1s));
1314
1315 BEAST_EXPECT(
1316 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
1317 env.close(5s);
1318 env(escrow::create(gw, carol, usd(1'000)),
1319 escrow::kFinishTime(env.now() + 1s),
1320 escrow::kCancelTime(env.now() + 2s),
1322 env.close(5s);
1323
1324 auto const ag = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
1325 BEAST_EXPECT(ag);
1326
1327 {
1328 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1329 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
1330 BEAST_EXPECT(
1331 // NOLINTNEXTLINE(modernize-use-ranges)
1332 std::find(aod.begin(), aod.end(), ag) != aod.end());
1333
1334 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1335 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1336
1337 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1338 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 3);
1339 BEAST_EXPECT(
1340 // NOLINTNEXTLINE(modernize-use-ranges)
1341 std::find(iod.begin(), iod.end(), ag) != iod.end());
1342 }
1343
1344 env.close(5s);
1345 env(escrow::finish(alice, alice, aseq));
1346 {
1347 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
1348
1349 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
1350 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
1351 BEAST_EXPECT(
1352 // NOLINTNEXTLINE(modernize-use-ranges)
1353 std::find(aod.begin(), aod.end(), ag) == aod.end());
1354
1355 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
1356 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
1357
1358 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
1359 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 2);
1360 BEAST_EXPECT(
1361 // NOLINTNEXTLINE(modernize-use-ranges)
1362 std::find(iod.begin(), iod.end(), ag) == iod.end());
1363 }
1364 }
1365 }
1366
1367 void
1369 {
1370 testcase("IOU RippleState");
1371 using namespace test::jtx;
1372 using namespace std::literals;
1373
1374 struct TestAccountData
1375 {
1376 Account src;
1377 Account dst;
1378 Account gw;
1379 bool hasTrustline;
1380 bool negative;
1381 };
1382
1384 // src > dst && src > issuer && dst no trustline
1385 {.src = Account("alice2"),
1386 .dst = Account("bob0"),
1387 .gw = Account{"gw0"},
1388 .hasTrustline = false,
1389 .negative = true},
1390 // src < dst && src < issuer && dst no trustline
1391 {.src = Account("carol0"),
1392 .dst = Account("dan1"),
1393 .gw = Account{"gw1"},
1394 .hasTrustline = false,
1395 .negative = false},
1396 // dst > src && dst > issuer && dst no trustline
1397 {.src = Account("dan1"),
1398 .dst = Account("alice2"),
1399 .gw = Account{"gw0"},
1400 .hasTrustline = false,
1401 .negative = true},
1402 // dst < src && dst < issuer && dst no trustline
1403 {.src = Account("bob0"),
1404 .dst = Account("carol0"),
1405 .gw = Account{"gw1"},
1406 .hasTrustline = false,
1407 .negative = false},
1408 // src > dst && src > issuer && dst has trustline
1409 {.src = Account("alice2"),
1410 .dst = Account("bob0"),
1411 .gw = Account{"gw0"},
1412 .hasTrustline = true,
1413 .negative = true},
1414 // src < dst && src < issuer && dst has trustline
1415 {.src = Account("carol0"),
1416 .dst = Account("dan1"),
1417 .gw = Account{"gw1"},
1418 .hasTrustline = true,
1419 .negative = false},
1420 // dst > src && dst > issuer && dst has trustline
1421 {.src = Account("dan1"),
1422 .dst = Account("alice2"),
1423 .gw = Account{"gw0"},
1424 .hasTrustline = true,
1425 .negative = true},
1426 // dst < src && dst < issuer && dst has trustline
1427 {.src = Account("bob0"),
1428 .dst = Account("carol0"),
1429 .gw = Account{"gw1"},
1430 .hasTrustline = true,
1431 .negative = false},
1432 }};
1433
1434 for (auto const& t : tests)
1435 {
1436 Env env{*this, features};
1437 auto const baseFee = env.current()->fees().base;
1438 auto const usd = t.gw["USD"];
1439 env.fund(XRP(5000), t.src, t.dst, t.gw);
1440 env(fset(t.gw, asfAllowTrustLineLocking));
1441 env.close();
1442
1443 if (t.hasTrustline)
1444 {
1445 env.trust(usd(100'000), t.src, t.dst);
1446 }
1447 else
1448 {
1449 env.trust(usd(100'000), t.src);
1450 }
1451 env.close();
1452
1453 env(pay(t.gw, t.src, usd(10'000)));
1454 if (t.hasTrustline)
1455 env(pay(t.gw, t.dst, usd(10'000)));
1456 env.close();
1457
1458 // src can create escrow
1459 auto const seq1 = env.seq(t.src);
1460 auto const delta = usd(1'000);
1461 env(escrow::create(t.src, t.dst, delta),
1463 escrow::kFinishTime(env.now() + 1s),
1464 Fee(baseFee * 150));
1465 env.close();
1466
1467 // dst can finish escrow
1468 auto const preSrc = env.balance(t.src, usd);
1469 auto const preDst = env.balance(t.dst, usd);
1470
1471 env(escrow::finish(t.dst, t.src, seq1),
1474 Fee(baseFee * 150));
1475 env.close();
1476
1477 BEAST_EXPECT(env.balance(t.src, usd) == preSrc);
1478 BEAST_EXPECT(env.balance(t.dst, usd) == preDst + delta);
1479 }
1480 }
1481
1482 void
1484 {
1485 testcase("IOU Gateway");
1486 using namespace test::jtx;
1487 using namespace std::literals;
1488
1489 struct TestAccountData
1490 {
1491 Account src;
1492 Account dst;
1493 bool hasTrustline;
1494 };
1495
1496 // issuer is source
1497 {
1498 auto const gw = Account{"gateway"};
1499 auto const alice = Account{"alice"};
1500 Env env{*this, features};
1501 auto const baseFee = env.current()->fees().base;
1502 auto const usd = gw["USD"];
1503 env.fund(XRP(5000), alice, gw);
1504 env(fset(gw, asfAllowTrustLineLocking));
1505 env.close();
1506 env.trust(usd(100'000), alice);
1507 env.close();
1508
1509 env(pay(gw, alice, usd(10'000)));
1510 env.close();
1511
1512 // issuer cannot create escrow
1513 env(escrow::create(gw, alice, usd(1'000)),
1515 escrow::kFinishTime(env.now() + 1s),
1516 Fee(baseFee * 150),
1518 env.close();
1519 }
1520
1521 std::array<TestAccountData, 4> const gwDstTests = {{
1522 // src > dst && src > issuer && dst has trustline
1523 {.src = Account("alice2"), .dst = Account{"gw0"}, .hasTrustline = true},
1524 // src < dst && src < issuer && dst has trustline
1525 {.src = Account("carol0"), .dst = Account{"gw1"}, .hasTrustline = true},
1526 // dst > src && dst > issuer && dst has trustline
1527 {.src = Account("dan1"), .dst = Account{"gw0"}, .hasTrustline = true},
1528 // dst < src && dst < issuer && dst has trustline
1529 {.src = Account("bob0"), .dst = Account{"gw1"}, .hasTrustline = true},
1530 }};
1531
1532 // issuer is destination
1533 for (auto const& t : gwDstTests)
1534 {
1535 Env env{*this, features};
1536 auto const baseFee = env.current()->fees().base;
1537 auto const usd = t.dst["USD"];
1538 env.fund(XRP(5000), t.dst, t.src);
1539 env(fset(t.dst, asfAllowTrustLineLocking));
1540 env.close();
1541
1542 env.trust(usd(100'000), t.src);
1543 env.close();
1544
1545 env(pay(t.dst, t.src, usd(10'000)));
1546 env.close();
1547
1548 // issuer can receive escrow
1549 auto const seq1 = env.seq(t.src);
1550 auto const preSrc = env.balance(t.src, usd);
1551 env(escrow::create(t.src, t.dst, usd(1'000)),
1553 escrow::kFinishTime(env.now() + 1s),
1554 Fee(baseFee * 150));
1555 env.close();
1556
1557 // issuer can finish escrow, no dest trustline
1558 env(escrow::finish(t.dst, t.src, seq1),
1561 Fee(baseFee * 150));
1562 env.close();
1563 auto const preAmount = 10'000;
1564 BEAST_EXPECT(preSrc == usd(preAmount));
1565 auto const postAmount = 9000;
1566 BEAST_EXPECT(env.balance(t.src, usd) == usd(postAmount));
1567 BEAST_EXPECT(env.balance(t.dst, usd) == usd(0));
1568 }
1569
1570 // issuer is source and destination
1571 {
1572 auto const gw = Account{"gateway"};
1573 auto const usd = gw["USD"];
1574 Env env{*this, features};
1575 auto const baseFee = env.current()->fees().base;
1576 env.fund(XRP(5000), gw);
1577 env(fset(gw, asfAllowTrustLineLocking));
1578 env.close();
1579
1580 // issuer cannot receive escrow
1581 env(escrow::create(gw, gw, usd(1'000)),
1583 escrow::kFinishTime(env.now() + 1s),
1584 Fee(baseFee * 150),
1586 env.close();
1587 }
1588 }
1589
1590 void
1592 {
1593 testcase("IOU Locked Rate");
1594 using namespace test::jtx;
1595 using namespace std::literals;
1596
1597 auto const alice = Account("alice");
1598 auto const bob = Account("bob");
1599 auto const carol = Account("carol");
1600 auto const gw = Account{"gateway"};
1601 auto const usd = gw["USD"];
1602
1603 // test locked rate
1604 {
1605 Env env{*this, features};
1606 auto const baseFee = env.current()->fees().base;
1607 env.fund(XRP(10'000), alice, bob, gw);
1608 env(fset(gw, asfAllowTrustLineLocking));
1609 env(rate(gw, 1.25));
1610 env.close();
1611 env.trust(usd(100'000), alice);
1612 env.trust(usd(100'000), bob);
1613 env.close();
1614 env(pay(gw, alice, usd(10'000)));
1615 env(pay(gw, bob, usd(10'000)));
1616 env.close();
1617
1618 // alice can create escrow w/ xfer rate
1619 auto const preAlice = env.balance(alice, usd);
1620 auto const seq1 = env.seq(alice);
1621 auto const delta = usd(125);
1622 env(escrow::create(alice, bob, delta),
1624 escrow::kFinishTime(env.now() + 1s),
1625 Fee(baseFee * 150));
1626 env.close();
1627 auto const transferRate = escrow::rate(env, alice, seq1);
1628 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1629
1630 // bob can finish escrow
1631 env(escrow::finish(bob, alice, seq1),
1634 Fee(baseFee * 150));
1635 env.close();
1636
1637 BEAST_EXPECT(env.balance(alice, usd) == preAlice - delta);
1638 BEAST_EXPECT(env.balance(bob, usd) == usd(10'100));
1639 }
1640 // test rate change - higher
1641 {
1642 Env env{*this, features};
1643 auto const baseFee = env.current()->fees().base;
1644 env.fund(XRP(10'000), alice, bob, gw);
1645 env(fset(gw, asfAllowTrustLineLocking));
1646 env(rate(gw, 1.25));
1647 env.close();
1648 env.trust(usd(100'000), alice);
1649 env.trust(usd(100'000), bob);
1650 env.close();
1651 env(pay(gw, alice, usd(10'000)));
1652 env(pay(gw, bob, usd(10'000)));
1653 env.close();
1654
1655 // alice can create escrow w/ xfer rate
1656 auto const preAlice = env.balance(alice, usd);
1657 auto const seq1 = env.seq(alice);
1658 auto const delta = usd(125);
1659 env(escrow::create(alice, bob, delta),
1661 escrow::kFinishTime(env.now() + 1s),
1662 Fee(baseFee * 150));
1663 env.close();
1664 auto transferRate = escrow::rate(env, alice, seq1);
1665 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1666
1667 // issuer changes rate higher
1668 env(rate(gw, 1.26));
1669 env.close();
1670
1671 // bob can finish escrow - rate unchanged
1672 env(escrow::finish(bob, alice, seq1),
1675 Fee(baseFee * 150));
1676 env.close();
1677
1678 BEAST_EXPECT(env.balance(alice, usd) == preAlice - delta);
1679 BEAST_EXPECT(env.balance(bob, usd) == usd(10'100));
1680 }
1681
1682 // test rate change - lower
1683 {
1684 Env env{*this, features};
1685 auto const baseFee = env.current()->fees().base;
1686 env.fund(XRP(10'000), alice, bob, gw);
1687 env(fset(gw, asfAllowTrustLineLocking));
1688 env(rate(gw, 1.25));
1689 env.close();
1690 env.trust(usd(100'000), alice);
1691 env.trust(usd(100'000), bob);
1692 env.close();
1693 env(pay(gw, alice, usd(10'000)));
1694 env(pay(gw, bob, usd(10'000)));
1695 env.close();
1696
1697 // alice can create escrow w/ xfer rate
1698 auto const preAlice = env.balance(alice, usd);
1699 auto const seq1 = env.seq(alice);
1700 auto const delta = usd(125);
1701 env(escrow::create(alice, bob, delta),
1703 escrow::kFinishTime(env.now() + 1s),
1704 Fee(baseFee * 150));
1705 env.close();
1706 auto transferRate = escrow::rate(env, alice, seq1);
1707 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1708
1709 // issuer changes rate lower
1710 env(rate(gw, 1.00));
1711 env.close();
1712
1713 // bob can finish escrow - rate changed
1714 env(escrow::finish(bob, alice, seq1),
1717 Fee(baseFee * 150));
1718 env.close();
1719
1720 BEAST_EXPECT(env.balance(alice, usd) == preAlice - delta);
1721 BEAST_EXPECT(env.balance(bob, usd) == usd(10125));
1722 }
1723
1724 // test cancel doesn't charge rate
1725 {
1726 Env env{*this, features};
1727 auto const baseFee = env.current()->fees().base;
1728 env.fund(XRP(10'000), alice, bob, gw);
1729 env(fset(gw, asfAllowTrustLineLocking));
1730 env(rate(gw, 1.25));
1731 env.close();
1732 env.trust(usd(100'000), alice);
1733 env.trust(usd(100'000), bob);
1734 env.close();
1735 env(pay(gw, alice, usd(10'000)));
1736 env(pay(gw, bob, usd(10'000)));
1737 env.close();
1738
1739 // alice can create escrow w/ xfer rate
1740 auto const preAlice = env.balance(alice, usd);
1741 auto const seq1 = env.seq(alice);
1742 auto const delta = usd(125);
1743 env(escrow::create(alice, bob, delta),
1744 escrow::kFinishTime(env.now() + 1s),
1745 escrow::kCancelTime(env.now() + 3s),
1746 Fee(baseFee));
1747 env.close();
1748 auto transferRate = escrow::rate(env, alice, seq1);
1749 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
1750
1751 // issuer changes rate lower
1752 env(rate(gw, 1.00));
1753 env.close();
1754
1755 // alice can cancel escrow - rate is not charged
1756 env(escrow::cancel(alice, alice, seq1), Fee(baseFee));
1757 env.close();
1758
1759 BEAST_EXPECT(env.balance(alice, usd) == preAlice);
1760 BEAST_EXPECT(env.balance(bob, usd) == usd(10000));
1761 }
1762 }
1763
1764 void
1766 {
1767 testcase("IOU Limit");
1768 using namespace test::jtx;
1769 using namespace std::literals;
1770
1771 auto const alice = Account("alice");
1772 auto const bob = Account("bob");
1773 auto const gw = Account{"gateway"};
1774 auto const usd = gw["USD"];
1775
1776 // test LimitAmount
1777 {
1778 Env env{*this, features};
1779 auto const baseFee = env.current()->fees().base;
1780 env.fund(XRP(1'000), alice, bob, gw);
1781 env(fset(gw, asfAllowTrustLineLocking));
1782 env.close();
1783 env.trust(usd(10'000), alice, bob);
1784 env.close();
1785 env(pay(gw, alice, usd(1'000)));
1786 env(pay(gw, bob, usd(1'000)));
1787 env.close();
1788
1789 // alice can create escrow
1790 auto seq1 = env.seq(alice);
1791 auto const delta = usd(125);
1792 env(escrow::create(alice, bob, delta),
1794 escrow::kFinishTime(env.now() + 1s),
1795 Fee(baseFee * 150));
1796 env.close();
1797
1798 // bob can finish
1799 auto const preBobLimit = env.limit(bob, usd);
1800 env(escrow::finish(bob, alice, seq1),
1803 Fee(baseFee * 150));
1804 env.close();
1805 auto const postBobLimit = env.limit(bob, usd);
1806 // bob's limit is NOT changed
1807 BEAST_EXPECT(postBobLimit == preBobLimit);
1808 }
1809 }
1810
1811 void
1813 {
1814 testcase("IOU Require Auth");
1815 using namespace test::jtx;
1816 using namespace std::literals;
1817
1818 auto const alice = Account("alice");
1819 auto const bob = Account("bob");
1820 auto const carol = Account("carol");
1821 auto const gw = Account{"gateway"};
1822 auto const usd = gw["USD"];
1823
1824 auto const aliceUSD = alice["USD"];
1825 auto const bobUSD = bob["USD"];
1826
1827 Env env{*this, features};
1828 auto const baseFee = env.current()->fees().base;
1829 env.fund(XRP(1'000), alice, bob, gw);
1830 env(fset(gw, asfAllowTrustLineLocking));
1831 env(fset(gw, asfRequireAuth));
1832 env.close();
1833 env(trust(gw, aliceUSD(10'000)), Txflags(tfSetfAuth));
1834 env(trust(alice, usd(10'000)));
1835 env(trust(bob, usd(10'000)));
1836 env.close();
1837 env(pay(gw, alice, usd(1'000)));
1838 env.close();
1839
1840 // alice cannot create escrow - fails without auth
1841 auto seq1 = env.seq(alice);
1842 auto const delta = usd(125);
1843 env(escrow::create(alice, bob, delta),
1845 escrow::kFinishTime(env.now() + 1s),
1846 Fee(baseFee * 150),
1847 Ter(tecNO_AUTH));
1848 env.close();
1849
1850 // set auth on bob
1851 env(trust(gw, bobUSD(10'000)), Txflags(tfSetfAuth));
1852 env(trust(bob, usd(10'000)));
1853 env.close();
1854 env(pay(gw, bob, usd(1'000)));
1855 env.close();
1856
1857 // alice can create escrow - bob has auth
1858 seq1 = env.seq(alice);
1859 env(escrow::create(alice, bob, delta),
1861 escrow::kFinishTime(env.now() + 1s),
1862 Fee(baseFee * 150));
1863 env.close();
1864
1865 // bob can finish
1866 env(escrow::finish(bob, alice, seq1),
1869 Fee(baseFee * 150));
1870 env.close();
1871 }
1872
1873 void
1875 {
1876 testcase("IOU Freeze");
1877 using namespace test::jtx;
1878 using namespace std::literals;
1879
1880 auto const alice = Account("alice");
1881 auto const bob = Account("bob");
1882 auto const carol = Account("carol");
1883 auto const gw = Account{"gateway"};
1884 auto const usd = gw["USD"];
1885
1886 // test Global Freeze
1887 {
1888 Env env{*this, features};
1889 auto const baseFee = env.current()->fees().base;
1890 env.fund(XRP(10'000), alice, bob, gw);
1891 env(fset(gw, asfAllowTrustLineLocking));
1892 env.close();
1893 env.trust(usd(100'000), alice);
1894 env.trust(usd(100'000), bob);
1895 env.close();
1896 env(pay(gw, alice, usd(10'000)));
1897 env(pay(gw, bob, usd(10'000)));
1898 env.close();
1899 env(fset(gw, asfGlobalFreeze));
1900 env.close();
1901
1902 // setup transaction
1903 auto seq1 = env.seq(alice);
1904 auto const delta = usd(125);
1905
1906 // create escrow fails - frozen trustline
1907 env(escrow::create(alice, bob, delta),
1909 escrow::kFinishTime(env.now() + 1s),
1910 Fee(baseFee * 150),
1911 Ter(tecFROZEN));
1912 env.close();
1913
1914 // clear global freeze
1915 env(fclear(gw, asfGlobalFreeze));
1916 env.close();
1917
1918 // create escrow success
1919 seq1 = env.seq(alice);
1920 env(escrow::create(alice, bob, delta),
1922 escrow::kFinishTime(env.now() + 1s),
1923 Fee(baseFee * 150));
1924 env.close();
1925
1926 // set global freeze
1927 env(fset(gw, asfGlobalFreeze));
1928 env.close();
1929
1930 // bob finish escrow success regardless of frozen assets
1931 env(escrow::finish(bob, alice, seq1),
1934 Fee(baseFee * 150));
1935 env.close();
1936
1937 // clear global freeze
1938 env(fclear(gw, asfGlobalFreeze));
1939 env.close();
1940
1941 // create escrow success
1942 seq1 = env.seq(alice);
1943 env(escrow::create(alice, bob, delta),
1945 escrow::kCancelTime(env.now() + 1s),
1946 Fee(baseFee * 150));
1947 env.close();
1948
1949 // set global freeze
1950 env(fset(gw, asfGlobalFreeze));
1951 env.close();
1952
1953 // bob cancel escrow success regardless of frozen assets
1954 env(escrow::cancel(bob, alice, seq1), Fee(baseFee));
1955 env.close();
1956 }
1957
1958 // test Individual Freeze
1959 {
1960 // Env Setup
1961 Env env{*this, features};
1962 auto const baseFee = env.current()->fees().base;
1963 env.fund(XRP(10'000), alice, bob, gw);
1964 env(fset(gw, asfAllowTrustLineLocking));
1965 env.close();
1966 env(trust(alice, usd(100'000)));
1967 env(trust(bob, usd(100'000)));
1968 env.close();
1969 env(pay(gw, alice, usd(10'000)));
1970 env(pay(gw, bob, usd(10'000)));
1971 env.close();
1972
1973 // set freeze on alice trustline
1974 env(trust(gw, usd(10'000), alice, tfSetFreeze));
1975 env.close();
1976
1977 // setup transaction
1978 auto seq1 = env.seq(alice);
1979 auto const delta = usd(125);
1980
1981 // create escrow fails - frozen trustline
1982 env(escrow::create(alice, bob, delta),
1984 escrow::kFinishTime(env.now() + 1s),
1985 Fee(baseFee * 150),
1986 Ter(tecFROZEN));
1987 env.close();
1988
1989 // clear freeze on alice trustline
1990 env(trust(gw, usd(10'000), alice, tfClearFreeze));
1991 env.close();
1992
1993 // create escrow success
1994 seq1 = env.seq(alice);
1995 env(escrow::create(alice, bob, delta),
1997 escrow::kFinishTime(env.now() + 1s),
1998 Fee(baseFee * 150));
1999 env.close();
2000
2001 // set freeze on bob trustline
2002 env(trust(gw, usd(10'000), bob, tfSetFreeze));
2003 env.close();
2004
2005 // bob finish escrow success regardless of frozen assets
2006 env(escrow::finish(bob, alice, seq1),
2009 Fee(baseFee * 150));
2010 env.close();
2011
2012 // reset freeze on bob and alice trustline
2013 env(trust(gw, usd(10'000), alice, tfClearFreeze));
2014 env(trust(gw, usd(10'000), bob, tfClearFreeze));
2015 env.close();
2016
2017 // create escrow success
2018 seq1 = env.seq(alice);
2019 env(escrow::create(alice, bob, delta),
2021 escrow::kCancelTime(env.now() + 1s),
2022 Fee(baseFee * 150));
2023 env.close();
2024
2025 // set freeze on bob trustline
2026 env(trust(gw, usd(10'000), bob, tfSetFreeze));
2027 env.close();
2028
2029 // bob cancel escrow success regardless of frozen assets
2030 env(escrow::cancel(bob, alice, seq1), Fee(baseFee));
2031 env.close();
2032 }
2033
2034 // test Deep Freeze
2035 {
2036 // Env Setup
2037 Env env{*this, features};
2038 auto const baseFee = env.current()->fees().base;
2039 env.fund(XRP(10'000), alice, bob, gw);
2040 env(fset(gw, asfAllowTrustLineLocking));
2041 env.close();
2042 env(trust(alice, usd(100'000)));
2043 env(trust(bob, usd(100'000)));
2044 env.close();
2045 env(pay(gw, alice, usd(10'000)));
2046 env(pay(gw, bob, usd(10'000)));
2047 env.close();
2048
2049 // set freeze on alice trustline
2050 env(trust(gw, usd(10'000), alice, tfSetFreeze | tfSetDeepFreeze));
2051 env.close();
2052
2053 // setup transaction
2054 auto seq1 = env.seq(alice);
2055 auto const delta = usd(125);
2056
2057 // create escrow fails - frozen trustline
2058 env(escrow::create(alice, bob, delta),
2060 escrow::kFinishTime(env.now() + 1s),
2061 Fee(baseFee * 150),
2062 Ter(tecFROZEN));
2063 env.close();
2064
2065 // clear freeze on alice trustline
2066 env(trust(gw, usd(10'000), alice, tfClearFreeze | tfClearDeepFreeze));
2067 env.close();
2068
2069 // create escrow success
2070 seq1 = env.seq(alice);
2071 env(escrow::create(alice, bob, delta),
2073 escrow::kFinishTime(env.now() + 1s),
2074 Fee(baseFee * 150));
2075 env.close();
2076
2077 // set freeze on bob trustline
2078 env(trust(gw, usd(10'000), bob, tfSetFreeze | tfSetDeepFreeze));
2079 env.close();
2080
2081 // bob finish escrow fails because of deep frozen assets
2082 env(escrow::finish(bob, alice, seq1),
2085 Fee(baseFee * 150),
2086 Ter(tecFROZEN));
2087 env.close();
2088
2089 // reset freeze on alice and bob trustline
2090 env(trust(gw, usd(10'000), alice, tfClearFreeze | tfClearDeepFreeze));
2091 env(trust(gw, usd(10'000), bob, tfClearFreeze | tfClearDeepFreeze));
2092 env.close();
2093
2094 // create escrow success
2095 seq1 = env.seq(alice);
2096 env(escrow::create(alice, bob, delta),
2098 escrow::kCancelTime(env.now() + 1s),
2099 Fee(baseFee * 150));
2100 env.close();
2101
2102 // set freeze on bob trustline
2103 env(trust(gw, usd(10'000), bob, tfSetFreeze | tfSetDeepFreeze));
2104 env.close();
2105
2106 // bob cancel escrow fails because of deep frozen assets
2107 env(escrow::cancel(bob, alice, seq1), Fee(baseFee), Ter(tesSUCCESS));
2108 env.close();
2109 }
2110 }
2111 void
2113 {
2114 testcase("IOU Insufficient Funds");
2115 using namespace test::jtx;
2116 using namespace std::literals;
2117
2118 auto const alice = Account("alice");
2119 auto const bob = Account("bob");
2120 auto const carol = Account("carol");
2121 auto const gw = Account{"gateway"};
2122 auto const usd = gw["USD"];
2123 {
2124 // test tecPATH_PARTIAL
2125 // ie. has 10'000, escrow 1'000 then try to pay 10'000
2126 Env env{*this, features};
2127 auto const baseFee = env.current()->fees().base;
2128 env.fund(XRP(10'000), alice, bob, gw);
2129 env(fset(gw, asfAllowTrustLineLocking));
2130 env.close();
2131 env.trust(usd(100'000), alice);
2132 env.trust(usd(100'000), bob);
2133 env.close();
2134 env(pay(gw, alice, usd(10'000)));
2135 env(pay(gw, bob, usd(10'000)));
2136 env.close();
2137
2138 // create escrow success
2139 auto const delta = usd(1'000);
2140 env(escrow::create(alice, bob, delta),
2142 escrow::kFinishTime(env.now() + 1s),
2143 Fee(baseFee * 150));
2144 env.close();
2145 env(pay(alice, gw, usd(10'000)), Ter(tecPATH_PARTIAL));
2146 }
2147 {
2148 // test tecINSUFFICIENT_FUNDS
2149 // ie. has 10'000 escrow 1'000 then try to escrow 10'000
2150 Env env{*this, features};
2151 auto const baseFee = env.current()->fees().base;
2152 env.fund(XRP(10'000), alice, bob, gw);
2153 env(fset(gw, asfAllowTrustLineLocking));
2154 env.close();
2155 env.trust(usd(100'000), alice);
2156 env.trust(usd(100'000), bob);
2157 env.close();
2158 env(pay(gw, alice, usd(10'000)));
2159 env(pay(gw, bob, usd(10'000)));
2160 env.close();
2161
2162 auto const delta = usd(1'000);
2163 env(escrow::create(alice, bob, delta),
2165 escrow::kFinishTime(env.now() + 1s),
2166 Fee(baseFee * 150));
2167 env.close();
2168
2169 env(escrow::create(alice, bob, usd(10'000)),
2171 escrow::kFinishTime(env.now() + 1s),
2172 Fee(baseFee * 150),
2174 env.close();
2175 }
2176 }
2177
2178 void
2180 {
2181 testcase("IOU Precision Loss");
2182 using namespace test::jtx;
2183 using namespace std::literals;
2184
2185 auto const alice = Account("alice");
2186 auto const bob = Account("bob");
2187 auto const gw = Account{"gateway"};
2188 auto const usd = gw["USD"];
2189
2190 // test min create precision loss
2191 {
2192 Env env(*this, features);
2193 auto const baseFee = env.current()->fees().base;
2194 env.fund(XRP(10'000), alice, bob, gw);
2195 env(fset(gw, asfAllowTrustLineLocking));
2196 env.close();
2197 env.trust(usd(100000000000000000), alice);
2198 env.trust(usd(100000000000000000), bob);
2199 env.close();
2200 env(pay(gw, alice, usd(10000000000000000)));
2201 env(pay(gw, bob, usd(1)));
2202 env.close();
2203
2204 bool const largeMantissa =
2205 features[featureSingleAssetVault] || features[featureLendingProtocol];
2206
2207 // alice cannot create escrow for 1/10 iou - precision loss
2208 env(escrow::create(alice, bob, usd(1)),
2210 escrow::kFinishTime(env.now() + 1s),
2211 Fee(baseFee * 150),
2212 Ter(largeMantissa ? (TER)tesSUCCESS : (TER)tecPRECISION_LOSS));
2213 env.close();
2214
2215 auto const seq1 = env.seq(alice);
2216 // alice can create escrow for 1'000 iou
2217 env(escrow::create(alice, bob, usd(1'000)),
2219 escrow::kFinishTime(env.now() + 1s),
2220 Fee(baseFee * 150));
2221 env.close();
2222
2223 // bob finish escrow success
2224 env(escrow::finish(bob, alice, seq1),
2227 Fee(baseFee * 150));
2228 env.close();
2229 }
2230 }
2231
2232 void
2234 {
2235 testcase("MPT Enablement");
2236
2237 using namespace jtx;
2238 using namespace std::chrono;
2239
2240 for (bool const withTokenEscrow : {false, true})
2241 {
2242 auto const amend = withTokenEscrow ? features : features - featureTokenEscrow;
2243 Env env{*this, amend};
2244 auto const baseFee = env.current()->fees().base;
2245 auto const alice = Account("alice");
2246 auto const bob = Account("bob");
2247 auto const gw = Account("gw");
2248 env.fund(XRP(5000), bob);
2249
2250 MPTTester mptGw(env, gw, {.holders = {alice}});
2251 mptGw.create(
2252 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2253 mptGw.authorize({.account = alice});
2254 auto const mpt = mptGw["MPT"];
2255 env(pay(gw, alice, mpt(10'000)));
2256 env.close();
2257
2258 auto const createResult = withTokenEscrow ? Ter(tesSUCCESS) : Ter(temBAD_AMOUNT);
2259 auto const finishResult = withTokenEscrow ? Ter(tesSUCCESS) : Ter(tecNO_TARGET);
2260
2261 auto const seq1 = env.seq(alice);
2262 env(escrow::create(alice, bob, mpt(1'000)),
2264 escrow::kFinishTime(env.now() + 1s),
2265 Fee(baseFee * 150),
2266 createResult);
2267 env.close();
2268 env(escrow::finish(bob, alice, seq1),
2271 Fee(baseFee * 150),
2272 finishResult);
2273 env.close();
2274 auto const seq2 = env.seq(alice);
2275 env(escrow::create(alice, bob, mpt(1'000)),
2277 escrow::kFinishTime(env.now() + 1s),
2278 escrow::kCancelTime(env.now() + 2s),
2279 Fee(baseFee * 150),
2280 createResult);
2281 env.close();
2282 env(escrow::cancel(bob, alice, seq2), finishResult);
2283 env.close();
2284 }
2285 }
2286
2287 void
2289 {
2290 testcase("MPT Create Preflight");
2291 using namespace test::jtx;
2292 using namespace std::literals;
2293
2294 for (bool const withMPT : {true, false})
2295 {
2296 auto const amend = withMPT ? features : features - featureMPTokensV1;
2297 Env env{*this, amend};
2298 auto const baseFee = env.current()->fees().base;
2299 auto const alice = Account("alice");
2300 auto const bob = Account("bob");
2301 auto const gw = Account("gw");
2302 env.fund(XRP(1'000), alice, bob, gw);
2303
2304 json::Value jv = escrow::create(alice, bob, XRP(1));
2305 jv.removeMember(jss::Amount);
2306 jv[jss::Amount][jss::mpt_issuance_id] =
2307 "00000004A407AF5856CCF3C42619DAA925813FC955C72983";
2308 jv[jss::Amount][jss::value] = "-1";
2309
2310 auto const result = withMPT ? Ter(temBAD_AMOUNT) : Ter(temDISABLED);
2311 env(jv,
2313 escrow::kFinishTime(env.now() + 1s),
2314 Fee(baseFee * 150),
2315 result);
2316 env.close();
2317 }
2318
2319 // temBAD_AMOUNT: amount < 0
2320 {
2321 Env env{*this, features};
2322 auto const baseFee = env.current()->fees().base;
2323 auto const alice = Account("alice");
2324 auto const bob = Account("bob");
2325 auto const gw = Account("gw");
2326
2327 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2328 mptGw.create(
2329 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2330 mptGw.authorize({.account = alice});
2331 mptGw.authorize({.account = bob});
2332 auto const mpt = mptGw["MPT"];
2333 env(pay(gw, alice, mpt(10'000)));
2334 env(pay(gw, bob, mpt(10'000)));
2335 env.close();
2336
2337 env(escrow::create(alice, bob, mpt(-1)),
2339 escrow::kFinishTime(env.now() + 1s),
2340 Fee(baseFee * 150),
2342 env.close();
2343 }
2344 }
2345
2346 void
2348 {
2349 testcase("MPT Create Preclaim");
2350 using namespace test::jtx;
2351 using namespace std::literals;
2352
2353 // tecNO_PERMISSION: issuer is the same as the account
2354 {
2355 Env env{*this, features};
2356 auto const baseFee = env.current()->fees().base;
2357 auto const alice = Account("alice");
2358 auto const gw = Account("gw");
2359
2360 MPTTester mptGw(env, gw, {.holders = {alice}});
2361 mptGw.create(
2362 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2363 mptGw.authorize({.account = alice});
2364 auto const mpt = mptGw["MPT"];
2365 env(pay(gw, alice, mpt(10'000)));
2366 env.close();
2367
2368 env(escrow::create(gw, alice, mpt(1)),
2370 escrow::kFinishTime(env.now() + 1s),
2371 Fee(baseFee * 150),
2373 env.close();
2374 }
2375
2376 // tecOBJECT_NOT_FOUND: mpt does not exist
2377 {
2378 Env env{*this, features};
2379 auto const baseFee = env.current()->fees().base;
2380 auto const alice = Account("alice");
2381 auto const bob = Account("bob");
2382 auto const gw = Account("gw");
2383 env.fund(XRP(10'000), alice, bob, gw);
2384 env.close();
2385
2386 auto const mpt = xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice));
2387 json::Value jv = escrow::create(alice, bob, mpt(2));
2388 jv[jss::Amount][jss::mpt_issuance_id] =
2389 "00000004A407AF5856CCF3C42619DAA925813FC955C72983";
2390 env(jv,
2392 escrow::kFinishTime(env.now() + 1s),
2393 Fee(baseFee * 150),
2395 env.close();
2396 }
2397
2398 // tecNO_PERMISSION: tfMPTCanEscrow is not enabled
2399 {
2400 Env env{*this, features};
2401 auto const baseFee = env.current()->fees().base;
2402 auto const alice = Account("alice");
2403 auto const bob = Account("bob");
2404 auto const gw = Account("gw");
2405
2406 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2407 mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer});
2408 mptGw.authorize({.account = alice});
2409 mptGw.authorize({.account = bob});
2410 auto const mpt = mptGw["MPT"];
2411 env(pay(gw, alice, mpt(10'000)));
2412 env(pay(gw, bob, mpt(10'000)));
2413 env.close();
2414
2415 env(escrow::create(alice, bob, mpt(3)),
2417 escrow::kFinishTime(env.now() + 1s),
2418 Fee(baseFee * 150),
2420 env.close();
2421 }
2422
2423 // tecOBJECT_NOT_FOUND: account does not have the mpt
2424 {
2425 Env env{*this, features};
2426 auto const baseFee = env.current()->fees().base;
2427 auto const alice = Account("alice");
2428 auto const bob = Account("bob");
2429 auto const gw = Account("gw");
2430
2431 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2432 mptGw.create(
2433 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2434 auto const mpt = mptGw["MPT"];
2435
2436 env(escrow::create(alice, bob, mpt(4)),
2438 escrow::kFinishTime(env.now() + 1s),
2439 Fee(baseFee * 150),
2441 env.close();
2442 }
2443
2444 // tecNO_AUTH: requireAuth set: account not authorized
2445 {
2446 Env env{*this, features};
2447 auto const baseFee = env.current()->fees().base;
2448 auto const alice = Account("alice");
2449 auto const bob = Account("bob");
2450 auto const gw = Account("gw");
2451
2452 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2453 mptGw.create(
2454 {.ownerCount = 1,
2455 .holderCount = 0,
2456 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2457 mptGw.authorize({.account = alice});
2458 mptGw.authorize({.account = gw, .holder = alice});
2459 auto const mpt = mptGw["MPT"];
2460 env(pay(gw, alice, mpt(10'000)));
2461 env.close();
2462
2463 // unauthorize account
2464 mptGw.authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
2465
2466 env(escrow::create(alice, bob, mpt(5)),
2468 escrow::kFinishTime(env.now() + 1s),
2469 Fee(baseFee * 150),
2470 Ter(tecNO_AUTH));
2471 env.close();
2472 }
2473
2474 // tecNO_AUTH: requireAuth set: dest not authorized
2475 {
2476 Env env{*this, features};
2477 auto const baseFee = env.current()->fees().base;
2478 auto const alice = Account("alice");
2479 auto const bob = Account("bob");
2480 auto const gw = Account("gw");
2481
2482 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2483 mptGw.create(
2484 {.ownerCount = 1,
2485 .holderCount = 0,
2486 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2487 mptGw.authorize({.account = alice});
2488 mptGw.authorize({.account = gw, .holder = alice});
2489 mptGw.authorize({.account = bob});
2490 mptGw.authorize({.account = gw, .holder = bob});
2491 auto const mpt = mptGw["MPT"];
2492 env(pay(gw, alice, mpt(10'000)));
2493 env(pay(gw, bob, mpt(10'000)));
2494 env.close();
2495
2496 // unauthorize dest
2497 mptGw.authorize({.account = gw, .holder = bob, .flags = tfMPTUnauthorize});
2498
2499 env(escrow::create(alice, bob, mpt(6)),
2501 escrow::kFinishTime(env.now() + 1s),
2502 Fee(baseFee * 150),
2503 Ter(tecNO_AUTH));
2504 env.close();
2505 }
2506
2507 // tecLOCKED: issuer has locked the account
2508 {
2509 Env env{*this, features};
2510 auto const baseFee = env.current()->fees().base;
2511 auto const alice = Account("alice");
2512 auto const bob = Account("bob");
2513 auto const gw = Account("gw");
2514
2515 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2516 mptGw.create(
2517 {.ownerCount = 1,
2518 .holderCount = 0,
2519 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
2520 mptGw.authorize({.account = alice});
2521 mptGw.authorize({.account = bob});
2522 auto const mpt = mptGw["MPT"];
2523 env(pay(gw, alice, mpt(10'000)));
2524 env(pay(gw, bob, mpt(10'000)));
2525 env.close();
2526
2527 // lock account
2528 mptGw.set({.account = gw, .holder = alice, .flags = tfMPTLock});
2529
2530 env(escrow::create(alice, bob, mpt(7)),
2532 escrow::kFinishTime(env.now() + 1s),
2533 Fee(baseFee * 150),
2534 Ter(tecLOCKED));
2535 env.close();
2536 }
2537
2538 // tecLOCKED: issuer has locked the dest
2539 {
2540 Env env{*this, features};
2541 auto const baseFee = env.current()->fees().base;
2542 auto const alice = Account("alice");
2543 auto const bob = Account("bob");
2544 auto const gw = Account("gw");
2545
2546 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2547 mptGw.create(
2548 {.ownerCount = 1,
2549 .holderCount = 0,
2550 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
2551 mptGw.authorize({.account = alice});
2552 mptGw.authorize({.account = bob});
2553 auto const mpt = mptGw["MPT"];
2554 env(pay(gw, alice, mpt(10'000)));
2555 env(pay(gw, bob, mpt(10'000)));
2556 env.close();
2557
2558 // lock dest
2559 mptGw.set({.account = gw, .holder = bob, .flags = tfMPTLock});
2560
2561 env(escrow::create(alice, bob, mpt(8)),
2563 escrow::kFinishTime(env.now() + 1s),
2564 Fee(baseFee * 150),
2565 Ter(tecLOCKED));
2566 env.close();
2567 }
2568
2569 // tecNO_AUTH: mpt cannot be transferred
2570 {
2571 Env env{*this, features};
2572 auto const baseFee = env.current()->fees().base;
2573 auto const alice = Account("alice");
2574 auto const bob = Account("bob");
2575 auto const gw = Account("gw");
2576
2577 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2578 mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow});
2579 mptGw.authorize({.account = alice});
2580 mptGw.authorize({.account = bob});
2581 auto const mpt = mptGw["MPT"];
2582 env(pay(gw, alice, mpt(10'000)));
2583 env(pay(gw, bob, mpt(10'000)));
2584 env.close();
2585
2586 env(escrow::create(alice, bob, mpt(9)),
2588 escrow::kFinishTime(env.now() + 1s),
2589 Fee(baseFee * 150),
2590 Ter(tecNO_AUTH));
2591 env.close();
2592 }
2593
2594 // tecINSUFFICIENT_FUNDS: spendable amount is zero
2595 {
2596 Env env{*this, features};
2597 auto const baseFee = env.current()->fees().base;
2598 auto const alice = Account("alice");
2599 auto const bob = Account("bob");
2600 auto const gw = Account("gw");
2601
2602 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2603 mptGw.create(
2604 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2605 mptGw.authorize({.account = alice});
2606 mptGw.authorize({.account = bob});
2607 auto const mpt = mptGw["MPT"];
2608 env(pay(gw, bob, mpt(10)));
2609 env.close();
2610
2611 env(escrow::create(alice, bob, mpt(11)),
2613 escrow::kFinishTime(env.now() + 1s),
2614 Fee(baseFee * 150),
2616 env.close();
2617 }
2618
2619 // tecINSUFFICIENT_FUNDS: spendable amount is less than the amount
2620 {
2621 Env env{*this, features};
2622 auto const baseFee = env.current()->fees().base;
2623 auto const alice = Account("alice");
2624 auto const bob = Account("bob");
2625 auto const gw = Account("gw");
2626
2627 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2628 mptGw.create(
2629 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2630 mptGw.authorize({.account = alice});
2631 mptGw.authorize({.account = bob});
2632 auto const mpt = mptGw["MPT"];
2633 env(pay(gw, alice, mpt(10)));
2634 env(pay(gw, bob, mpt(10)));
2635 env.close();
2636
2637 env(escrow::create(alice, bob, mpt(11)),
2639 escrow::kFinishTime(env.now() + 1s),
2640 Fee(baseFee * 150),
2642 env.close();
2643 }
2644 }
2645
2646 void
2648 {
2649 testcase("MPT Finish Preclaim");
2650 using namespace test::jtx;
2651 using namespace std::literals;
2652
2653 // tecNO_AUTH: requireAuth set: dest not authorized
2654 {
2655 Env env{*this, features};
2656 auto const baseFee = env.current()->fees().base;
2657 auto const alice = Account("alice");
2658 auto const bob = Account("bob");
2659 auto const gw = Account("gw");
2660
2661 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2662 mptGw.create(
2663 {.ownerCount = 1,
2664 .holderCount = 0,
2665 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2666 mptGw.authorize({.account = alice});
2667 mptGw.authorize({.account = gw, .holder = alice});
2668 mptGw.authorize({.account = bob});
2669 mptGw.authorize({.account = gw, .holder = bob});
2670 auto const mpt = mptGw["MPT"];
2671 env(pay(gw, alice, mpt(10'000)));
2672 env(pay(gw, bob, mpt(10'000)));
2673 env.close();
2674
2675 auto const seq1 = env.seq(alice);
2676 env(escrow::create(alice, bob, mpt(10)),
2678 escrow::kFinishTime(env.now() + 1s),
2679 Fee(baseFee * 150),
2680 Ter(tesSUCCESS));
2681 env.close();
2682
2683 // unauthorize dest
2684 mptGw.authorize({.account = gw, .holder = bob, .flags = tfMPTUnauthorize});
2685
2686 env(escrow::finish(bob, alice, seq1),
2689 Fee(baseFee * 150),
2690 Ter(tecNO_AUTH));
2691 env.close();
2692 }
2693
2694 // tecOBJECT_NOT_FOUND: MPT issuance does not exist
2695 {
2696 Env env{*this, features};
2697 auto const baseFee = env.current()->fees().base;
2698 auto const alice = Account("alice");
2699 auto const bob = Account("bob");
2700 env.fund(XRP(10'000), alice, bob);
2701 env.close();
2702
2703 auto const seq1 = env.seq(alice);
2704 env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
2705 Sandbox sb(&view, TapNone);
2706 auto sleNew =
2708 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
2709 STAmount const amt(mpt, 10);
2710 sleNew->setAccountID(sfDestination, bob);
2711 sleNew->setFieldAmount(sfAmount, amt);
2712 sb.insert(sleNew);
2713 sb.apply(view);
2714 return true;
2715 });
2716
2717 env(escrow::finish(bob, alice, seq1),
2720 Fee(baseFee * 150),
2722 env.close();
2723 }
2724
2725 // tecLOCKED: issuer has locked the dest
2726 {
2727 Env env{*this, features};
2728 auto const baseFee = env.current()->fees().base;
2729 auto const alice = Account("alice");
2730 auto const bob = Account("bob");
2731 auto const gw = Account("gw");
2732
2733 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2734 mptGw.create(
2735 {.ownerCount = 1,
2736 .holderCount = 0,
2737 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
2738 mptGw.authorize({.account = alice});
2739 mptGw.authorize({.account = bob});
2740 auto const mpt = mptGw["MPT"];
2741 env(pay(gw, alice, mpt(10'000)));
2742 env(pay(gw, bob, mpt(10'000)));
2743 env.close();
2744
2745 auto const seq1 = env.seq(alice);
2746 env(escrow::create(alice, bob, mpt(8)),
2748 escrow::kFinishTime(env.now() + 1s),
2749 Fee(baseFee * 150),
2750 Ter(tesSUCCESS));
2751 env.close();
2752
2753 // lock dest
2754 mptGw.set({.account = gw, .holder = bob, .flags = tfMPTLock});
2755
2756 env(escrow::finish(bob, alice, seq1),
2759 Fee(baseFee * 150),
2760 Ter(tecLOCKED));
2761 env.close();
2762 }
2763 }
2764
2765 void
2767 {
2768 testcase("MPT Finish Do Apply");
2769 using namespace test::jtx;
2770 using namespace std::literals;
2771
2772 // tecINSUFFICIENT_RESERVE: insufficient reserve to create MPT
2773 {
2774 Env env{*this, features};
2775 auto const baseFee = env.current()->fees().base;
2776 auto const acctReserve = env.current()->fees().reserve;
2777 auto const incReserve = env.current()->fees().increment;
2778
2779 auto const alice = Account("alice");
2780 auto const bob = Account("bob");
2781 auto const gw = Account("gw");
2782 env.fund(acctReserve + (incReserve - 1), bob);
2783 env.close();
2784
2785 MPTTester mptGw(env, gw, {.holders = {alice}});
2786 mptGw.create(
2787 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2788 mptGw.authorize({.account = alice});
2789 auto const mpt = mptGw["MPT"];
2790 env(pay(gw, alice, mpt(10'000)));
2791 env.close();
2792
2793 auto const seq1 = env.seq(alice);
2794 env(escrow::create(alice, bob, mpt(10)),
2796 escrow::kFinishTime(env.now() + 1s),
2797 Fee(baseFee * 150),
2798 Ter(tesSUCCESS));
2799 env.close();
2800
2801 env(escrow::finish(bob, alice, seq1),
2804 Fee(baseFee * 150),
2806 env.close();
2807 }
2808
2809 // tesSUCCESS: bob submits; finish MPT created
2810 {
2811 Env env{*this, features};
2812 auto const baseFee = env.current()->fees().base;
2813 auto const alice = Account("alice");
2814 auto const bob = Account("bob");
2815 auto const gw = Account("gw");
2816 env.fund(XRP(10'000), bob);
2817 env.close();
2818
2819 MPTTester mptGw(env, gw, {.holders = {alice}});
2820 mptGw.create(
2821 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2822 mptGw.authorize({.account = alice});
2823 auto const mpt = mptGw["MPT"];
2824 env(pay(gw, alice, mpt(10'000)));
2825 env.close();
2826
2827 auto const seq1 = env.seq(alice);
2828 env(escrow::create(alice, bob, mpt(10)),
2830 escrow::kFinishTime(env.now() + 1s),
2831 Fee(baseFee * 150),
2832 Ter(tesSUCCESS));
2833 env.close();
2834
2835 env(escrow::finish(bob, alice, seq1),
2838 Fee(baseFee * 150),
2839 Ter(tesSUCCESS));
2840 env.close();
2841 }
2842
2843 // tecNO_PERMISSION: carol submits; finish MPT not created
2844 {
2845 Env env{*this, features};
2846 auto const baseFee = env.current()->fees().base;
2847 auto const alice = Account("alice");
2848 auto const bob = Account("bob");
2849 auto const carol = Account("carol");
2850 auto const gw = Account("gw");
2851 env.fund(XRP(10'000), bob, carol);
2852 env.close();
2853
2854 MPTTester mptGw(env, gw, {.holders = {alice}});
2855 mptGw.create(
2856 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2857 mptGw.authorize({.account = alice});
2858 auto const mpt = mptGw["MPT"];
2859 env(pay(gw, alice, mpt(10'000)));
2860 env.close();
2861
2862 auto const seq1 = env.seq(alice);
2863 env(escrow::create(alice, bob, mpt(10)),
2865 escrow::kFinishTime(env.now() + 1s),
2866 Fee(baseFee * 150),
2867 Ter(tesSUCCESS));
2868 env.close();
2869
2870 env(escrow::finish(carol, alice, seq1),
2873 Fee(baseFee * 150),
2875 env.close();
2876 }
2877 }
2878
2879 void
2881 {
2882 testcase("MPT Cancel Preclaim");
2883 using namespace test::jtx;
2884 using namespace std::literals;
2885
2886 // tecNO_AUTH: requireAuth set: account not authorized
2887 {
2888 Env env{*this, features};
2889 auto const baseFee = env.current()->fees().base;
2890 auto const alice = Account("alice");
2891 auto const bob = Account("bob");
2892 auto const gw = Account("gw");
2893
2894 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
2895 mptGw.create(
2896 {.ownerCount = 1,
2897 .holderCount = 0,
2898 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
2899 mptGw.authorize({.account = alice});
2900 mptGw.authorize({.account = gw, .holder = alice});
2901 mptGw.authorize({.account = bob});
2902 mptGw.authorize({.account = gw, .holder = bob});
2903 auto const mpt = mptGw["MPT"];
2904 env(pay(gw, alice, mpt(10'000)));
2905 env(pay(gw, bob, mpt(10'000)));
2906 env.close();
2907
2908 auto const seq1 = env.seq(alice);
2909 env(escrow::create(alice, bob, mpt(10)),
2910 escrow::kCancelTime(env.now() + 2s),
2912 Fee(baseFee * 150),
2913 Ter(tesSUCCESS));
2914 env.close();
2915
2916 // unauthorize account
2917 mptGw.authorize({.account = gw, .holder = alice, .flags = tfMPTUnauthorize});
2918
2919 env(escrow::cancel(bob, alice, seq1), Ter(tecNO_AUTH));
2920 env.close();
2921 }
2922
2923 // tecOBJECT_NOT_FOUND: MPT issuance does not exist
2924 {
2925 Env env{*this, features};
2926 auto const baseFee = env.current()->fees().base;
2927 auto const alice = Account("alice");
2928 auto const bob = Account("bob");
2929 env.fund(XRP(10'000), alice, bob);
2930
2931 auto const seq1 = env.seq(alice);
2932 env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
2933 Sandbox sb(&view, TapNone);
2934 auto sleNew =
2936 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
2937 STAmount const amt(mpt, 10);
2938 sleNew->setAccountID(sfDestination, bob);
2939 sleNew->setFieldAmount(sfAmount, amt);
2940 sb.insert(sleNew);
2941 sb.apply(view);
2942 return true;
2943 });
2944
2945 env(escrow::cancel(bob, alice, seq1), Fee(baseFee), Ter(tecOBJECT_NOT_FOUND));
2946 env.close();
2947 }
2948 }
2949
2950 void
2952 {
2953 testcase("MPT Balances");
2954
2955 using namespace jtx;
2956 using namespace std::chrono;
2957
2958 Env env{*this, features};
2959 auto const baseFee = env.current()->fees().base;
2960 auto const alice = Account("alice");
2961 auto const bob = Account("bob");
2962 auto const carol = Account("carol");
2963 auto const gw = Account("gw");
2964 env.fund(XRP(5000), bob);
2965
2966 MPTTester mptGw(env, gw, {.holders = {alice, carol}});
2967 mptGw.create(
2968 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
2969 mptGw.authorize({.account = alice});
2970 mptGw.authorize({.account = carol});
2971 auto const mpt = mptGw["MPT"];
2972 env(pay(gw, alice, mpt(10'000)));
2973 env(pay(gw, carol, mpt(10'000)));
2974 env.close();
2975
2976 auto outstandingMPT = env.balance(gw, mpt);
2977
2978 // Create & Finish Escrow
2979 auto const seq1 = env.seq(alice);
2980 {
2981 auto const preAliceMPT = env.balance(alice, mpt);
2982 auto const preBobMPT = env.balance(bob, mpt);
2983 env(escrow::create(alice, bob, mpt(1'000)),
2985 escrow::kFinishTime(env.now() + 1s),
2986 Fee(baseFee * 150),
2987 Ter(tesSUCCESS));
2988 env.close();
2989
2990 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
2991 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
2992 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
2993 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
2994 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
2995 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
2996 }
2997 {
2998 auto const preAliceMPT = env.balance(alice, mpt);
2999 auto const preBobMPT = env.balance(bob, mpt);
3000 env(escrow::finish(bob, alice, seq1),
3003 Fee(baseFee * 150),
3004 Ter(tesSUCCESS));
3005 env.close();
3006
3007 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT);
3008 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3009 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT + mpt(1'000));
3010 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3011 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3012 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3013 }
3014
3015 // Create & Cancel Escrow
3016 auto const seq2 = env.seq(alice);
3017 {
3018 auto const preAliceMPT = env.balance(alice, mpt);
3019 auto const preBobMPT = env.balance(bob, mpt);
3020 env(escrow::create(alice, bob, mpt(1'000)),
3022 escrow::kFinishTime(env.now() + 1s),
3023 escrow::kCancelTime(env.now() + 2s),
3024 Fee(baseFee * 150),
3025 Ter(tesSUCCESS));
3026 env.close();
3027
3028 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3029 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3030 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3031 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3032 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3033 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
3034 }
3035 {
3036 auto const preAliceMPT = env.balance(alice, mpt);
3037 auto const preBobMPT = env.balance(bob, mpt);
3038 env(escrow::cancel(bob, alice, seq2), Ter(tesSUCCESS));
3039 env.close();
3040
3041 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT + mpt(1'000));
3042 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3043 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3044 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3045 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3046 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3047 }
3048
3049 // Self Escrow Create & Finish
3050 {
3051 auto const seq = env.seq(alice);
3052 auto const preAliceMPT = env.balance(alice, mpt);
3053 env(escrow::create(alice, alice, mpt(1'000)),
3055 escrow::kFinishTime(env.now() + 1s),
3056 Fee(baseFee * 150),
3057 Ter(tesSUCCESS));
3058 env.close();
3059
3060 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3061 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3062 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3063 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
3064
3065 env(escrow::finish(alice, alice, seq),
3068 Fee(baseFee * 150),
3069 Ter(tesSUCCESS));
3070 env.close();
3071
3072 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT);
3073 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3074 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3075 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3076 }
3077
3078 // Self Escrow Create & Cancel
3079 {
3080 auto const seq = env.seq(alice);
3081 auto const preAliceMPT = env.balance(alice, mpt);
3082 env(escrow::create(alice, alice, mpt(1'000)),
3084 escrow::kFinishTime(env.now() + 1s),
3085 escrow::kCancelTime(env.now() + 2s),
3086 Fee(baseFee * 150),
3087 Ter(tesSUCCESS));
3088 env.close();
3089
3090 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3091 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3092 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3093 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1'000);
3094
3095 env(escrow::cancel(alice, alice, seq), Ter(tesSUCCESS));
3096 env.close();
3097
3098 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT);
3099 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3100 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3101 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3102 }
3103
3104 // Multiple Escrows
3105 {
3106 auto const preAliceMPT = env.balance(alice, mpt);
3107 auto const preBobMPT = env.balance(bob, mpt);
3108 auto const preCarolMPT = env.balance(carol, mpt);
3109 env(escrow::create(alice, bob, mpt(1'000)),
3111 escrow::kFinishTime(env.now() + 1s),
3112 Fee(baseFee * 150),
3113 Ter(tesSUCCESS));
3114 env.close();
3115
3116 env(escrow::create(carol, bob, mpt(1'000)),
3118 escrow::kFinishTime(env.now() + 1s),
3119 Fee(baseFee * 150),
3120 Ter(tesSUCCESS));
3121 env.close();
3122
3123 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3124 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3125 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3126 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3127 BEAST_EXPECT(env.balance(carol, mpt) == preCarolMPT - mpt(1'000));
3128 BEAST_EXPECT(mptEscrowed(env, carol, mpt) == 1'000);
3129 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3130 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 2'000);
3131 }
3132
3133 // Max MPT Amount Issued (Escrow 1 MPT)
3134 {
3135 Env env{*this, features};
3136 auto const baseFee = env.current()->fees().base;
3137 auto const alice = Account("alice");
3138 auto const bob = Account("bob");
3139 auto const gw = Account("gw");
3140
3141 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3142 mptGw.create(
3143 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3144 mptGw.authorize({.account = alice});
3145 mptGw.authorize({.account = bob});
3146 auto const mpt = mptGw["MPT"];
3147 env(pay(gw, alice, mpt(kMaxMpTokenAmount)));
3148 env.close();
3149
3150 auto const preAliceMPT = env.balance(alice, mpt);
3151 auto const preBobMPT = env.balance(bob, mpt);
3152 auto const outstandingMPT = env.balance(gw, mpt);
3153
3154 auto const seq1 = env.seq(alice);
3155 env(escrow::create(alice, bob, mpt(1)),
3157 escrow::kFinishTime(env.now() + 1s),
3158 Fee(baseFee * 150));
3159 env.close();
3160
3161 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1));
3162 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1);
3163 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3164 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3165 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3166 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 1);
3167
3168 env(escrow::finish(bob, alice, seq1),
3171 Fee(baseFee * 150),
3172 Ter(tesSUCCESS));
3173 env.close();
3174
3175 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1));
3176 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3177 BEAST_EXPECT(
3178 !env.le(keylet::mptoken(mpt.mpt(), alice))->isFieldPresent(sfLockedAmount));
3179 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT + mpt(1));
3180 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3181 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3182 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3183 BEAST_EXPECT(
3184 !env.le(keylet::mptokenIssuance(mpt.mpt()))->isFieldPresent(sfLockedAmount));
3185 }
3186
3187 // Max MPT Amount Issued (Escrow Max MPT)
3188 {
3189 Env env{*this, features};
3190 auto const baseFee = env.current()->fees().base;
3191 auto const alice = Account("alice");
3192 auto const bob = Account("bob");
3193 auto const gw = Account("gw");
3194
3195 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3196 mptGw.create(
3197 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3198 mptGw.authorize({.account = alice});
3199 mptGw.authorize({.account = bob});
3200 auto const mpt = mptGw["MPT"];
3201 env(pay(gw, alice, mpt(kMaxMpTokenAmount)));
3202 env.close();
3203
3204 auto const preAliceMPT = env.balance(alice, mpt);
3205 auto const preBobMPT = env.balance(bob, mpt);
3206 auto const outstandingMPT = env.balance(gw, mpt);
3207
3208 // Escrow Max MPT - 10
3209 auto const seq1 = env.seq(alice);
3210 env(escrow::create(alice, bob, mpt(kMaxMpTokenAmount - 10)),
3212 escrow::kFinishTime(env.now() + 1s),
3213 Fee(baseFee * 150));
3214 env.close();
3215
3216 // Escrow 10 MPT
3217 auto const seq2 = env.seq(alice);
3218 env(escrow::create(alice, bob, mpt(10)),
3220 escrow::kFinishTime(env.now() + 1s),
3221 Fee(baseFee * 150));
3222 env.close();
3223
3224 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(kMaxMpTokenAmount));
3225 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == kMaxMpTokenAmount);
3226 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT);
3227 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3228 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3229 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == kMaxMpTokenAmount);
3230
3231 env(escrow::finish(bob, alice, seq1),
3234 Fee(baseFee * 150),
3235 Ter(tesSUCCESS));
3236 env.close();
3237
3238 env(escrow::finish(bob, alice, seq2),
3241 Fee(baseFee * 150),
3242 Ter(tesSUCCESS));
3243 env.close();
3244
3245 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(kMaxMpTokenAmount));
3246 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3247 BEAST_EXPECT(env.balance(bob, mpt) == preBobMPT + mpt(kMaxMpTokenAmount));
3248 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
3249 BEAST_EXPECT(env.balance(gw, mpt) == outstandingMPT);
3250 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3251 }
3252 }
3253
3254 void
3256 {
3257 using namespace jtx;
3258 using namespace std::chrono;
3259
3260 auto const alice = Account("alice");
3261 auto const bob = Account("bob");
3262 auto const carol = Account("carol");
3263 auto const gw = Account{"gateway"};
3264 {
3265 testcase("MPT Metadata to self");
3266
3267 Env env{*this, features};
3268 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3269 mptGw.create(
3270 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3271 mptGw.authorize({.account = alice});
3272 mptGw.authorize({.account = bob});
3273 auto const mpt = mptGw["MPT"];
3274 env(pay(gw, alice, mpt(10'000)));
3275 env(pay(gw, bob, mpt(10'000)));
3276 env.close();
3277 auto const aseq = env.seq(alice);
3278 auto const bseq = env.seq(bob);
3279
3280 env(escrow::create(alice, alice, mpt(1'000)),
3281 escrow::kFinishTime(env.now() + 1s),
3282 escrow::kCancelTime(env.now() + 500s));
3283 BEAST_EXPECT(
3284 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3285 env.close(5s);
3286 auto const aa = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
3287 BEAST_EXPECT(aa);
3288 {
3289 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3290 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
3291 BEAST_EXPECT(
3292 // NOLINTNEXTLINE(modernize-use-ranges)
3293 std::find(aod.begin(), aod.end(), aa) != aod.end());
3294 }
3295
3296 {
3297 xrpl::Dir const iod(*env.current(), keylet::ownerDir(gw.id()));
3298 BEAST_EXPECT(std::distance(iod.begin(), iod.end()) == 1);
3299 BEAST_EXPECT(
3300 // NOLINTNEXTLINE(modernize-use-ranges)
3301 std::find(iod.begin(), iod.end(), aa) == iod.end());
3302 }
3303
3304 env(escrow::create(bob, bob, mpt(1'000)),
3305 escrow::kFinishTime(env.now() + 1s),
3306 escrow::kCancelTime(env.now() + 2s));
3307 BEAST_EXPECT(
3308 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3309 env.close(5s);
3310 auto const bb = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
3311 BEAST_EXPECT(bb);
3312
3313 {
3314 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3315 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
3316 BEAST_EXPECT(
3317 // NOLINTNEXTLINE(modernize-use-ranges)
3318 std::find(bod.begin(), bod.end(), bb) != bod.end());
3319 }
3320
3321 env.close(5s);
3322 env(escrow::finish(alice, alice, aseq));
3323 {
3324 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
3325 BEAST_EXPECT(
3326 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3327
3328 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3329 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
3330 BEAST_EXPECT(
3331 // NOLINTNEXTLINE(modernize-use-ranges)
3332 std::find(aod.begin(), aod.end(), aa) == aod.end());
3333
3334 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3335 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
3336 BEAST_EXPECT(
3337 // NOLINTNEXTLINE(modernize-use-ranges)
3338 std::find(bod.begin(), bod.end(), bb) != bod.end());
3339 }
3340
3341 env.close(5s);
3342 env(escrow::cancel(bob, bob, bseq));
3343 {
3344 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
3345 BEAST_EXPECT(
3346 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3347
3348 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3349 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
3350 BEAST_EXPECT(
3351 // NOLINTNEXTLINE(modernize-use-ranges)
3352 std::find(bod.begin(), bod.end(), bb) == bod.end());
3353 }
3354 }
3355
3356 {
3357 testcase("MPT Metadata to other");
3358
3359 Env env{*this, features};
3360 MPTTester mptGw(env, gw, {.holders = {alice, bob, carol}});
3361 mptGw.create(
3362 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3363 mptGw.authorize({.account = alice});
3364 mptGw.authorize({.account = bob});
3365 mptGw.authorize({.account = carol});
3366 auto const mpt = mptGw["MPT"];
3367 env(pay(gw, alice, mpt(10'000)));
3368 env(pay(gw, bob, mpt(10'000)));
3369 env(pay(gw, carol, mpt(10'000)));
3370 env.close();
3371 auto const aseq = env.seq(alice);
3372 auto const bseq = env.seq(bob);
3373
3374 env(escrow::create(alice, bob, mpt(1'000)), escrow::kFinishTime(env.now() + 1s));
3375 BEAST_EXPECT(
3376 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3377 env.close(5s);
3378 env(escrow::create(bob, carol, mpt(1'000)),
3379 escrow::kFinishTime(env.now() + 1s),
3380 escrow::kCancelTime(env.now() + 2s));
3381 BEAST_EXPECT(
3382 (*env.meta())[sfTransactionResult] == static_cast<std::uint8_t>(tesSUCCESS));
3383 env.close(5s);
3384
3385 auto const ab = env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq)));
3386 BEAST_EXPECT(ab);
3387
3388 auto const bc = env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq)));
3389 BEAST_EXPECT(bc);
3390
3391 {
3392 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3393 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 2);
3394 BEAST_EXPECT(
3395 // NOLINTNEXTLINE(modernize-use-ranges)
3396 std::find(aod.begin(), aod.end(), ab) != aod.end());
3397
3398 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3399 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 3);
3400 BEAST_EXPECT(
3401 // NOLINTNEXTLINE(modernize-use-ranges)
3402 std::find(bod.begin(), bod.end(), ab) != bod.end());
3403 BEAST_EXPECT(
3404 // NOLINTNEXTLINE(modernize-use-ranges)
3405 std::find(bod.begin(), bod.end(), bc) != bod.end());
3406
3407 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
3408 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
3409 BEAST_EXPECT(
3410 // NOLINTNEXTLINE(modernize-use-ranges)
3411 std::find(cod.begin(), cod.end(), bc) != cod.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(env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
3419
3420 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3421 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
3422 BEAST_EXPECT(
3423 // NOLINTNEXTLINE(modernize-use-ranges)
3424 std::find(aod.begin(), aod.end(), ab) == aod.end());
3425
3426 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3427 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 2);
3428 BEAST_EXPECT(
3429 // NOLINTNEXTLINE(modernize-use-ranges)
3430 std::find(bod.begin(), bod.end(), ab) == bod.end());
3431 BEAST_EXPECT(
3432 // NOLINTNEXTLINE(modernize-use-ranges)
3433 std::find(bod.begin(), bod.end(), bc) != bod.end());
3434
3435 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
3436 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 2);
3437 }
3438
3439 env.close(5s);
3440 env(escrow::cancel(bob, bob, bseq));
3441 {
3442 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(aseq))));
3443 BEAST_EXPECT(!env.le(keylet::escrow(bob.id(), SeqProxy::rawSequence(bseq))));
3444
3445 xrpl::Dir const aod(*env.current(), keylet::ownerDir(alice.id()));
3446 BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 1);
3447 BEAST_EXPECT(
3448 // NOLINTNEXTLINE(modernize-use-ranges)
3449 std::find(aod.begin(), aod.end(), ab) == aod.end());
3450
3451 xrpl::Dir const bod(*env.current(), keylet::ownerDir(bob.id()));
3452 BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 1);
3453 BEAST_EXPECT(
3454 // NOLINTNEXTLINE(modernize-use-ranges)
3455 std::find(bod.begin(), bod.end(), ab) == bod.end());
3456 BEAST_EXPECT(
3457 // NOLINTNEXTLINE(modernize-use-ranges)
3458 std::find(bod.begin(), bod.end(), bc) == bod.end());
3459
3460 xrpl::Dir const cod(*env.current(), keylet::ownerDir(carol.id()));
3461 BEAST_EXPECT(std::distance(cod.begin(), cod.end()) == 1);
3462 }
3463 }
3464 }
3465
3466 void
3468 {
3469 testcase("MPT Gateway Balances");
3470 using namespace test::jtx;
3471 using namespace std::literals;
3472
3473 // issuer is dest; alice w/ authorization
3474 {
3475 Env env{*this, features};
3476 auto const baseFee = env.current()->fees().base;
3477 auto const alice = Account("alice");
3478 auto const gw = Account("gw");
3479
3480 MPTTester mptGw(env, gw, {.holders = {alice}});
3481 mptGw.create(
3482 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3483 mptGw.authorize({.account = alice});
3484 auto const mpt = mptGw["MPT"];
3485 env(pay(gw, alice, mpt(10'000)));
3486 env.close();
3487
3488 // issuer can be destination
3489 auto const seq1 = env.seq(alice);
3490 auto const preAliceMPT = env.balance(alice, mpt);
3491 auto const preOutstanding = env.balance(gw, mpt);
3492 auto const preEscrowed = issuerMPTEscrowed(env, mpt);
3493 BEAST_EXPECT(preOutstanding == mpt(-10'000));
3494 BEAST_EXPECT(preEscrowed == 0);
3495
3496 env(escrow::create(alice, gw, mpt(1'000)),
3498 escrow::kFinishTime(env.now() + 1s),
3499 Fee(baseFee * 150));
3500 env.close();
3501
3502 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3503 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 1'000);
3504 BEAST_EXPECT(env.balance(gw, mpt) == preOutstanding);
3505 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == preEscrowed + 1'000);
3506
3507 // issuer (dest) can finish escrow
3508 env(escrow::finish(gw, alice, seq1),
3511 Fee(baseFee * 150));
3512 env.close();
3513
3514 BEAST_EXPECT(env.balance(alice, mpt) == preAliceMPT - mpt(1'000));
3515 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3516 BEAST_EXPECT(env.balance(gw, mpt) == preOutstanding + mpt(1'000));
3517 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == preEscrowed);
3518 }
3519 }
3520
3521 void
3523 {
3524 testcase("MPT Locked Rate");
3525 using namespace test::jtx;
3526 using namespace std::literals;
3527
3528 auto const alice = Account("alice");
3529 auto const bob = Account("bob");
3530 auto const carol = Account("carol");
3531 auto const gw = Account{"gateway"};
3532 auto const usd = gw["USD"];
3533
3534 // test locked rate: finish
3535 {
3536 Env env{*this, features};
3537 auto const baseFee = env.current()->fees().base;
3538 auto const alice = Account("alice");
3539 auto const bob = Account("bob");
3540 auto const gw = Account("gw");
3541
3542 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3543 mptGw.create(
3544 {.transferFee = 25000,
3545 .ownerCount = 1,
3546 .holderCount = 0,
3547 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3548 mptGw.authorize({.account = alice});
3549 mptGw.authorize({.account = bob});
3550 auto const mpt = mptGw["MPT"];
3551 env(pay(gw, alice, mpt(10'000)));
3552 env(pay(gw, bob, mpt(10'000)));
3553 env.close();
3554
3555 // alice can create escrow w/ xfer rate
3556 auto const preAlice = env.balance(alice, mpt);
3557 auto const seq1 = env.seq(alice);
3558 auto const delta = mpt(125);
3559 env(escrow::create(alice, bob, mpt(125)),
3561 escrow::kFinishTime(env.now() + 1s),
3562 Fee(baseFee * 150));
3563 env.close();
3564 auto const transferRate = escrow::rate(env, alice, seq1);
3565 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
3566
3567 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 125);
3568 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 125);
3569 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-20'000));
3570
3571 // bob can finish escrow
3572 env(escrow::finish(bob, alice, seq1),
3575 Fee(baseFee * 150));
3576 env.close();
3577
3578 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - delta);
3579 BEAST_EXPECT(env.balance(bob, mpt) == mpt(10'100));
3580
3581 auto const escrowedWithFix = env.current()->rules().enabled(fixTokenEscrowV1) ? 0 : 25;
3582 auto const outstandingWithFix =
3583 env.current()->rules().enabled(fixTokenEscrowV1) ? mpt(19'975) : mpt(20'000);
3584 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowedWithFix);
3585 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowedWithFix);
3586 BEAST_EXPECT(env.balance(gw, mpt) == -outstandingWithFix);
3587 }
3588
3589 // test locked rate: cancel
3590 {
3591 Env env{*this, features};
3592 auto const baseFee = env.current()->fees().base;
3593 auto const alice = Account("alice");
3594 auto const bob = Account("bob");
3595 auto const gw = Account("gw");
3596
3597 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3598 mptGw.create(
3599 {.transferFee = 25000,
3600 .ownerCount = 1,
3601 .holderCount = 0,
3602 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3603 mptGw.authorize({.account = alice});
3604 mptGw.authorize({.account = bob});
3605 auto const mpt = mptGw["MPT"];
3606 env(pay(gw, alice, mpt(10'000)));
3607 env(pay(gw, bob, mpt(10'000)));
3608 env.close();
3609
3610 // alice can create escrow w/ xfer rate
3611 auto const preAlice = env.balance(alice, mpt);
3612 auto const preBob = env.balance(bob, mpt);
3613 auto const seq1 = env.seq(alice);
3614 auto const delta = mpt(125);
3615 env(escrow::create(alice, bob, mpt(125)),
3617 escrow::kFinishTime(env.now() + 1s),
3618 escrow::kCancelTime(env.now() + 3s),
3619 Fee(baseFee * 150));
3620 env.close();
3621 auto const transferRate = escrow::rate(env, alice, seq1);
3622 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
3623
3624 // alice can cancel escrow
3625 env(escrow::cancel(alice, alice, seq1), Fee(baseFee));
3626 env.close();
3627
3628 BEAST_EXPECT(env.balance(alice, mpt) == preAlice);
3629 BEAST_EXPECT(env.balance(bob, mpt) == preBob);
3630 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-20'000));
3631 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3632 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3633 }
3634
3635 // test locked rate: issuer is destination
3636 {
3637 Env env{*this, features};
3638 auto const baseFee = env.current()->fees().base;
3639 auto const alice = Account("alice");
3640 auto const bob = Account("bob");
3641 auto const gw = Account("gw");
3642
3643 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3644 mptGw.create(
3645 {.transferFee = 25000,
3646 .ownerCount = 1,
3647 .holderCount = 0,
3648 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3649 mptGw.authorize({.account = alice});
3650 mptGw.authorize({.account = bob});
3651 auto const mpt = mptGw["MPT"];
3652 env(pay(gw, alice, mpt(10'000)));
3653 env(pay(gw, bob, mpt(10'000)));
3654 env.close();
3655
3656 // alice can create escrow w/ xfer rate
3657 auto const preAlice = env.balance(alice, mpt);
3658 auto const seq1 = env.seq(alice);
3659 auto const delta = mpt(125);
3660 env(escrow::create(alice, gw, mpt(125)),
3662 escrow::kFinishTime(env.now() + 1s),
3663 Fee(baseFee * 150));
3664 env.close();
3665 auto const transferRate = escrow::rate(env, alice, seq1);
3666 BEAST_EXPECT(transferRate.value == std::uint32_t(1'000'000'000 * 1.25));
3667
3668 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 125);
3669 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 125);
3670 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-20'000));
3671
3672 // bob can finish escrow
3673 env(escrow::finish(gw, alice, seq1),
3676 Fee(baseFee * 150));
3677 env.close();
3678
3679 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - delta);
3680 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3681 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3682 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-19'875));
3683 }
3684 }
3685
3686 void
3688 {
3689 using namespace test::jtx;
3690 using namespace std::literals;
3691
3692 bool const withCleanup340 = features[fixCleanup3_4_0];
3693 testcase(
3694 std::string("MPT Split Escrow Transfer Fee ") +
3695 (withCleanup340 ? "with Cleanup340" : "without Cleanup340"));
3696
3697 Env env{*this, features};
3698 auto const baseFee = env.current()->fees().base;
3699 auto const alice = Account("alice");
3700 auto const bob = Account("bob");
3701 auto const gw = Account("gw");
3702 env.fund(XRP(1'000), alice, bob, gw);
3703 env.close();
3704
3705 MPTTester const mpt({
3706 .env = env,
3707 .issuer = gw,
3708 .holders = {alice, bob},
3709 .transferFee = 1'000,
3710 .flags = tfMPTCanEscrow | tfMPTCanTransfer,
3711 });
3712 env(pay(gw, alice, mpt(10'000)));
3713 env.close();
3714
3715 static constexpr int escrowCount = 10;
3716 static constexpr int splitAmount = 10;
3717 static constexpr int totalLocked = escrowCount * splitAmount;
3719 for (auto& seq : seqs)
3720 {
3721 seq = env.seq(alice);
3722 env(escrow::create(alice, bob, mpt(splitAmount)),
3724 escrow::kFinishTime(env.now() + 1s),
3725 Fee(baseFee * 150));
3726 env.close();
3727 }
3728
3729 BEAST_EXPECT(env.balance(alice, mpt) == mpt(10'000 - totalLocked));
3730 BEAST_EXPECT(env.balance(bob, mpt) == mpt(0));
3731 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-10'000));
3732 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == totalLocked);
3733 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == totalLocked);
3734
3735 for (auto const seq : seqs)
3736 {
3737 env(escrow::finish(bob, alice, seq),
3740 Fee(baseFee * 150));
3741 env.close();
3742 }
3743
3744 auto const feeBurned = withCleanup340 ? escrowCount : 0;
3745 BEAST_EXPECT(env.balance(alice, mpt) == mpt(10'000 - totalLocked));
3746 BEAST_EXPECT(env.balance(bob, mpt) == mpt(totalLocked - feeBurned));
3747 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-10'000 + feeBurned));
3748 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3749 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3750 }
3751
3752 void
3754 {
3755 testcase("MPT large locked rate");
3756 using namespace test::jtx;
3757 using namespace std::literals;
3758
3759 auto constexpr escrowAmount = 200'000'000'000'000'000LL;
3760 auto constexpr noOverflowEscrowAmount = 186'000'000'000'000'000LL;
3761 auto const alice = Account("alice");
3762 auto const bob = Account("bob");
3763 auto const gw = Account("gw");
3764
3765 for (auto const testFeatures :
3766 {features - featureMPTokensV2 - fixCleanup3_4_0,
3767 features - featureMPTokensV2,
3768 (features | featureMPTokensV2) - fixCleanup3_4_0,
3769 features | featureMPTokensV2})
3770 {
3771 bool const mptV2 = testFeatures[featureMPTokensV2];
3772 bool const tokenEscrowV1 = testFeatures[fixTokenEscrowV1];
3773 // The transfer-fee split in EscrowFinish only overflows on the
3774 // legacy divideRound(amount, lockedRate, ...) path, which runs when
3775 // fixCleanup3_4_0 is disabled. With fixCleanup3_4_0 the split uses
3776 // mulRatio (128-bit intermediate), which cannot overflow. Without
3777 // it, this large amount overflows unless the MPTokensV2 Number path
3778 // is active. So the finish succeeds when either amendment is enabled.
3779 bool const cleanup340 = testFeatures[fixCleanup3_4_0];
3780 bool const noOverflow = cleanup340 || mptV2;
3781 auto const expectedErr = noOverflow ? Ter(tesSUCCESS) : Ter(tefEXCEPTION);
3782
3783 // Finish with a large MPT amount and non-zero transfer fee. When the
3784 // computation overflows (legacy divideRound path, no MPTokensV2) the
3785 // finish fails with tefEXCEPTION and the escrow is untouched;
3786 // otherwise it unlocks the escrow.
3787 {
3788 Env env{*this, testFeatures};
3789 env.fund(XRP(1'000), alice, bob, gw);
3790 auto const baseFee = env.current()->fees().base;
3791
3792 MPTTester const mpt(
3793 {.env = env,
3794 .issuer = gw,
3795 .holders = {alice, bob},
3796 .transferFee = 1'000,
3797 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3798 env(pay(gw, alice, mpt(escrowAmount)));
3799 env.close();
3800
3801 auto const preAlice = env.balance(alice, mpt);
3802 auto const preBob = env.balance(bob, mpt);
3803 auto const seq = env.seq(alice);
3804 env(escrow::create(alice, bob, mpt(escrowAmount)),
3806 escrow::kFinishTime(env.now() + 1s),
3807 escrow::kCancelTime(env.now() + 500s),
3808 Fee(baseFee * 150));
3809 env.close();
3810
3811 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowAmount);
3812 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowAmount);
3813
3814 env(escrow::finish(bob, alice, seq),
3817 Fee(baseFee * 150),
3818 expectedErr);
3819 env.close();
3820
3821 if (noOverflow)
3822 {
3823 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
3824 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - mpt(escrowAmount));
3825 auto const postBob = env.balance(bob, mpt);
3826 BEAST_EXPECT(postBob.value() > preBob.value());
3827 BEAST_EXPECT(postBob.value() < (preBob + mpt(escrowAmount)).value());
3828 auto const xferFee = escrowAmount - (postBob.value() - preBob.value());
3829 auto const expectedEscrow = tokenEscrowV1 ? 0 : xferFee;
3830 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == expectedEscrow);
3831 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == expectedEscrow);
3832 }
3833 else
3834 {
3835 BEAST_EXPECT(env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
3836 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - mpt(escrowAmount));
3837 BEAST_EXPECT(env.balance(bob, mpt) == preBob);
3838 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowAmount);
3839 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowAmount);
3840 }
3841 }
3842
3843 // Control: a still-large amount below the legacy overflow boundary
3844 // finishes successfully in both feature modes.
3845 {
3846 Env env{*this, testFeatures};
3847 env.fund(XRP(1'000), alice, bob, gw);
3848 auto const baseFee = env.current()->fees().base;
3849
3850 MPTTester const mpt(
3851 {.env = env,
3852 .issuer = gw,
3853 .holders = {alice, bob},
3854 .transferFee = 1'000,
3855 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3856 env(pay(gw, alice, mpt(noOverflowEscrowAmount)));
3857 env.close();
3858
3859 auto const preAlice = env.balance(alice, mpt);
3860 auto const preBob = env.balance(bob, mpt);
3861 auto const seq = env.seq(alice);
3862 env(escrow::create(alice, bob, mpt(noOverflowEscrowAmount)),
3864 escrow::kFinishTime(env.now() + 1s),
3865 escrow::kCancelTime(env.now() + 500s),
3866 Fee(baseFee * 150));
3867 env.close();
3868
3869 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == noOverflowEscrowAmount);
3870 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == noOverflowEscrowAmount);
3871
3872 env(escrow::finish(bob, alice, seq),
3875 Fee(baseFee * 150),
3876 Ter(tesSUCCESS));
3877 env.close();
3878
3879 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
3880 BEAST_EXPECT(env.balance(alice, mpt) == preAlice - mpt(noOverflowEscrowAmount));
3881 auto const postBob = env.balance(bob, mpt);
3882 BEAST_EXPECT(postBob.value() > preBob.value());
3883 BEAST_EXPECT(postBob.value() < (preBob + mpt(noOverflowEscrowAmount)).value());
3884 auto const xferFee = noOverflowEscrowAmount - (postBob.value() - preBob.value());
3885 auto const expectedEscrow = tokenEscrowV1 ? 0 : xferFee;
3886 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == expectedEscrow);
3887 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == expectedEscrow);
3888 }
3889
3890 // Cancel returns the escrow to the owner using parity rate, so it
3891 // does not hit the transfer-rate division in either feature mode.
3892 {
3893 Env env{*this, testFeatures};
3894 env.fund(XRP(1'000), alice, bob, gw);
3895 auto const baseFee = env.current()->fees().base;
3896
3897 MPTTester const mpt(
3898 {.env = env,
3899 .issuer = gw,
3900 .holders = {alice, bob},
3901 .transferFee = 1'000,
3902 .flags = tfMPTCanEscrow | tfMPTCanTransfer});
3903 env(pay(gw, alice, mpt(escrowAmount)));
3904 env.close();
3905
3906 auto const preAlice = env.balance(alice, mpt);
3907 auto const preBob = env.balance(bob, mpt);
3908 auto const seq = env.seq(alice);
3909 env(escrow::create(alice, bob, mpt(escrowAmount)),
3911 escrow::kFinishTime(env.now() + 1s),
3912 escrow::kCancelTime(env.now() + 3s),
3913 Fee(baseFee * 150));
3914 env.close();
3915
3916 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == escrowAmount);
3917 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == escrowAmount);
3918
3919 env(escrow::cancel(alice, alice, seq), Fee(baseFee), Ter(tesSUCCESS));
3920 env.close();
3921
3922 BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
3923 BEAST_EXPECT(env.balance(alice, mpt) == preAlice);
3924 BEAST_EXPECT(env.balance(bob, mpt) == preBob);
3925 BEAST_EXPECT(env.balance(gw, mpt) == -mpt(escrowAmount));
3926 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
3927 BEAST_EXPECT(issuerMPTEscrowed(env, mpt) == 0);
3928 }
3929 }
3930 }
3931
3932 void
3934 {
3935 testcase("MPT Require Auth");
3936 using namespace test::jtx;
3937 using namespace std::literals;
3938
3939 Env env{*this, features};
3940 auto const baseFee = env.current()->fees().base;
3941 auto const alice = Account("alice");
3942 auto const bob = Account("bob");
3943 auto const gw = Account("gw");
3944
3945 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3946 mptGw.create(
3947 {.ownerCount = 1,
3948 .holderCount = 0,
3949 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTRequireAuth});
3950 mptGw.authorize({.account = alice});
3951 mptGw.authorize({.account = gw, .holder = alice});
3952 mptGw.authorize({.account = bob});
3953 mptGw.authorize({.account = gw, .holder = bob});
3954 auto const mpt = mptGw["MPT"];
3955 env(pay(gw, alice, mpt(10'000)));
3956 env.close();
3957
3958 auto seq = env.seq(alice);
3959 auto const delta = mpt(125);
3960 // alice can create escrow - is authorized
3961 env(escrow::create(alice, bob, mpt(100)),
3963 escrow::kFinishTime(env.now() + 1s),
3964 Fee(baseFee * 150));
3965 env.close();
3966
3967 // bob can finish escrow - is authorized
3968 env(escrow::finish(bob, alice, seq),
3971 Fee(baseFee * 150));
3972 env.close();
3973 }
3974
3975 void
3977 {
3978 testcase("MPT Lock");
3979 using namespace test::jtx;
3980 using namespace std::literals;
3981
3982 Env env{*this, features};
3983 auto const baseFee = env.current()->fees().base;
3984 auto const alice = Account("alice");
3985 auto const bob = Account("bob");
3986 auto const gw = Account("gw");
3987
3988 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
3989 mptGw.create(
3990 {.ownerCount = 1,
3991 .holderCount = 0,
3992 .flags = tfMPTCanEscrow | tfMPTCanTransfer | tfMPTCanLock});
3993 mptGw.authorize({.account = alice});
3994 mptGw.authorize({.account = bob});
3995 auto const mpt = mptGw["MPT"];
3996 env(pay(gw, alice, mpt(10'000)));
3997 env(pay(gw, bob, mpt(10'000)));
3998 env.close();
3999
4000 // alice create escrow
4001 auto seq1 = env.seq(alice);
4002 env(escrow::create(alice, bob, mpt(100)),
4004 escrow::kFinishTime(env.now() + 1s),
4005 escrow::kCancelTime(env.now() + 2s),
4006 Fee(baseFee * 150));
4007 env.close();
4008
4009 // lock account & dest
4010 mptGw.set({.account = gw, .holder = alice, .flags = tfMPTLock});
4011 mptGw.set({.account = gw, .holder = bob, .flags = tfMPTLock});
4012
4013 // bob cannot finish
4014 env(escrow::finish(bob, alice, seq1),
4017 Fee(baseFee * 150),
4018 Ter(tecLOCKED));
4019 env.close();
4020
4021 // bob can cancel
4022 env(escrow::cancel(bob, alice, seq1));
4023 env.close();
4024 }
4025
4026 void
4028 {
4029 testcase("MPT Can Transfer");
4030 using namespace test::jtx;
4031 using namespace std::literals;
4032
4033 Env env{*this, features};
4034 auto const baseFee = env.current()->fees().base;
4035 auto const alice = Account("alice");
4036 auto const bob = Account("bob");
4037 auto const gw = Account("gw");
4038
4039 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
4040 mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow});
4041 mptGw.authorize({.account = alice});
4042 mptGw.authorize({.account = bob});
4043 auto const mpt = mptGw["MPT"];
4044 env(pay(gw, alice, mpt(10'000)));
4045 env(pay(gw, bob, mpt(10'000)));
4046 env.close();
4047
4048 // alice cannot create escrow to non issuer
4049 env(escrow::create(alice, bob, mpt(100)),
4051 escrow::kFinishTime(env.now() + 1s),
4052 escrow::kCancelTime(env.now() + 2s),
4053 Fee(baseFee * 150),
4054 Ter(tecNO_AUTH));
4055 env.close();
4056
4057 // Escrow Create & Finish
4058 {
4059 // alice an create escrow to issuer
4060 auto seq = env.seq(alice);
4061 env(escrow::create(alice, gw, mpt(100)),
4063 escrow::kFinishTime(env.now() + 1s),
4064 Fee(baseFee * 150));
4065 env.close();
4066
4067 // gw can finish
4068 env(escrow::finish(gw, alice, seq),
4071 Fee(baseFee * 150));
4072 env.close();
4073 }
4074
4075 // Escrow Create & Cancel
4076 {
4077 // alice an create escrow to issuer
4078 auto seq = env.seq(alice);
4079 env(escrow::create(alice, gw, mpt(100)),
4081 escrow::kFinishTime(env.now() + 1s),
4082 escrow::kCancelTime(env.now() + 2s),
4083 Fee(baseFee * 150));
4084 env.close();
4085
4086 // alice can cancel
4087 env(escrow::cancel(alice, alice, seq));
4088 env.close();
4089 }
4090 }
4091
4092 void
4094 {
4095 testcase("MPT Destroy");
4096 using namespace test::jtx;
4097 using namespace std::literals;
4098
4099 // tecHAS_OBLIGATIONS: issuer cannot destroy issuance
4100 {
4101 Env env{*this, features};
4102 auto const baseFee = env.current()->fees().base;
4103 auto const alice = Account("alice");
4104 auto const bob = Account("bob");
4105 auto const gw = Account("gw");
4106
4107 MPTTester mptGw(env, gw, {.holders = {alice, bob}});
4108 mptGw.create(
4109 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
4110 mptGw.authorize({.account = alice});
4111 mptGw.authorize({.account = bob});
4112 auto const mpt = mptGw["MPT"];
4113 env(pay(gw, alice, mpt(10'000)));
4114 env(pay(gw, bob, mpt(10'000)));
4115 env.close();
4116
4117 auto const seq1 = env.seq(alice);
4118 env(escrow::create(alice, bob, mpt(10)),
4120 escrow::kFinishTime(env.now() + 1s),
4121 Fee(baseFee * 150));
4122 env.close();
4123
4124 env(pay(alice, gw, mpt(10'000)), Ter(tecPATH_PARTIAL));
4125 env(pay(alice, gw, mpt(9'990)));
4126 env(pay(bob, gw, mpt(10'000)));
4127 BEAST_EXPECT(env.balance(alice, mpt) == mpt(0));
4128 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 10);
4129 BEAST_EXPECT(env.balance(bob, mpt) == mpt(0));
4130 BEAST_EXPECT(mptEscrowed(env, bob, mpt) == 0);
4131 BEAST_EXPECT(env.balance(gw, mpt) == mpt(-10));
4132 mptGw.authorize({.account = bob, .flags = tfMPTUnauthorize});
4133 mptGw.destroy({.id = mptGw.issuanceID(), .ownerCount = 1, .err = tecHAS_OBLIGATIONS});
4134
4135 env(escrow::finish(bob, alice, seq1),
4138 Fee(baseFee * 150),
4139 Ter(tesSUCCESS));
4140 env.close();
4141
4142 env(pay(bob, gw, mpt(10)));
4143 mptGw.destroy({.id = mptGw.issuanceID(), .ownerCount = 0});
4144 }
4145
4146 // tecHAS_OBLIGATIONS: holder cannot destroy mptoken
4147 {
4148 Env env{*this, features};
4149 auto const baseFee = env.current()->fees().base;
4150 auto const alice = Account("alice");
4151 auto const bob = Account("bob");
4152 auto const gw = Account("gw");
4153 env.fund(XRP(10'000), bob);
4154 env.close();
4155
4156 MPTTester mptGw(env, gw, {.holders = {alice}});
4157 mptGw.create(
4158 {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer});
4159 mptGw.authorize({.account = alice});
4160 auto const mpt = mptGw["MPT"];
4161 env(pay(gw, alice, mpt(10'000)));
4162 env.close();
4163
4164 auto const seq1 = env.seq(alice);
4165 env(escrow::create(alice, bob, mpt(10)),
4167 escrow::kFinishTime(env.now() + 1s),
4168 Fee(baseFee * 150),
4169 Ter(tesSUCCESS));
4170 env.close();
4171
4172 env(pay(alice, gw, mpt(9'990)));
4173 env.close();
4174
4175 BEAST_EXPECT(env.balance(alice, mpt) == mpt(0));
4176 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 10);
4177 mptGw.authorize(
4178 {.account = alice, .flags = tfMPTUnauthorize, .err = tecHAS_OBLIGATIONS});
4179
4180 env(escrow::finish(bob, alice, seq1),
4183 Fee(baseFee * 150),
4184 Ter(tesSUCCESS));
4185 env.close();
4186
4187 BEAST_EXPECT(env.balance(alice, mpt) == mpt(0));
4188 BEAST_EXPECT(mptEscrowed(env, alice, mpt) == 0);
4189 mptGw.authorize({.account = alice, .flags = tfMPTUnauthorize});
4190 BEAST_EXPECT(!env.le(keylet::mptoken(mpt.mpt(), alice)));
4191 }
4192 }
4193
4194 void
4196 {
4197 testIOUEnablement(features);
4198 testIOUAllowLockingFlag(features);
4199 testIOUCreatePreflight(features);
4200 testIOUCreatePreclaim(features);
4201 testIOUFinishPreclaim(features);
4202 testIOUFinishDoApply(features);
4203 testIOUCancelPreclaim(features);
4204 testIOUCancelDoApply(features);
4205 testIOUBalances(features);
4206 testIOUMetaAndOwnership(features);
4207 testIOURippleState(features);
4208 testIOUGateway(features);
4209 testIOULockedRate(features);
4210 testIOULimitAmount(features);
4211 testIOURequireAuth(features);
4212 testIOUFreeze(features);
4213 testIOUInsufficientFunds(features);
4214 testIOUPrecisionLoss(features);
4215 }
4216
4217 void
4219 {
4220 testMPTEnablement(features);
4221 testMPTCreatePreflight(features);
4222 testMPTCreatePreclaim(features);
4223 testMPTFinishPreclaim(features);
4224 testMPTFinishDoApply(features);
4225 testMPTCancelPreclaim(features);
4226 testMPTBalances(features);
4227 testMPTMetaAndOwnership(features);
4228 testMPTGateway(features);
4229 testMPTLockedRate(features);
4230 testMPTLargeLockedRate(features);
4231 testMPTRequireAuth(features);
4232 testMPTLock(features);
4233 testMPTCanTransfer(features);
4234 testMPTDestroy(features);
4235 }
4236
4237public:
4238 void
4239 run() override
4240 {
4241 using namespace test::jtx;
4242 FeatureBitset const all{testableAmendments()};
4243 for (FeatureBitset const& feats :
4244 {all - featureSingleAssetVault - featureLendingProtocol, all})
4245 {
4246 testIOUWithFeats(feats);
4247 testIOUWithFeats(feats - fixCleanup3_2_0);
4248 testMPTWithFeats(feats);
4249 testMPTWithFeats(feats - fixTokenEscrowV1);
4250 }
4251 testMPTSplitEscrowTransferFee(all - fixCleanup3_4_0);
4253 }
4254};
4255
4256BEAST_DEFINE_TESTSUITE(EscrowToken, app, xrpl);
4257
4258} // 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(modify_type const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:59
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
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
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:1056
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:764
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:447
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:241
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:388
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:373
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:543
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:537
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:253
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
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:165
@ tefEXCEPTION
Definition TER.h:164
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:907
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
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:184
@ 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:647
@ tecLOCKED
Definition TER.h:361
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:295
@ tecPATH_PARTIAL
Definition TER.h:285
@ tecNO_TARGET
Definition TER.h:307
@ tecOBJECT_NOT_FOUND
Definition TER.h:329
@ tecNO_AUTH
Definition TER.h:303
@ tecFROZEN
Definition TER.h:306
@ tecINSUFFICIENT_FUNDS
Definition TER.h:328
@ tecNO_LINE
Definition TER.h:304
@ tecPRECISION_LOSS
Definition TER.h:366
@ tecINSUFFICIENT_RESERVE
Definition TER.h:310
@ tecLIMIT_EXCEEDED
Definition TER.h:364
@ tecNO_PERMISSION
Definition TER.h:308
@ tecNO_ISSUER
Definition TER.h:302
@ tecHAS_OBLIGATIONS
Definition TER.h:320
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:296
@ tesSUCCESS
Definition TER.h:245
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 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...