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