rippled
Loading...
Searching...
No Matches
AMMClawback_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2024 Ripple Labs Inc.
5 Permission to use, copy, modify, and/or distribute this software for any
6 purpose with or without fee is hereby granted, provided that the above
7 copyright notice and this permission notice appear in all copies.
8 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*/
16//==============================================================================
17
18#include <test/jtx.h>
19#include <test/jtx/AMM.h>
20#include <test/jtx/AMMTest.h>
21#include <test/jtx/trust.h>
22#include <xrpl/protocol/Feature.h>
23
24#include <initializer_list>
25
26namespace ripple {
27namespace test {
29{
30 void
32 {
33 testcase("test invalid request");
34 using namespace jtx;
35
36 // Test if holder does not exist.
37 {
38 Env env(*this, features);
39 Account gw{"gateway"};
40 Account alice{"alice"};
41 env.fund(XRP(100000), gw, alice);
42 env.close();
43
44 // gw sets asfAllowTrustLineClawback.
46 env.close();
48
49 env.trust(USD(10000), alice);
50 env(pay(gw, alice, gw["USD"](100)));
51
52 AMM amm(env, alice, XRP(100), USD(100));
53 env.close();
54
56 gw, Account("unknown"), USD, XRP, std::nullopt),
58 }
59
60 // Test if asset pair provided does not exist. This should
61 // return terNO_AMM error.
62 {
63 Env env(*this, features);
64 Account gw{"gateway"};
65 Account alice{"alice"};
66 env.fund(XRP(100000), gw, alice);
67 env.close();
68
69 // gw sets asfAllowTrustLineClawback.
71 env.close();
73
74 // gw issues 100 USD to Alice.
75 auto const USD = gw["USD"];
76 env.trust(USD(10000), alice);
77 env(pay(gw, alice, USD(100)));
78 env.close();
79
80 // Withdraw all the tokens from the AMMAccount.
81 // The AMMAccount will be auto deleted.
82 AMM amm(env, gw, XRP(100), USD(100));
83 amm.withdrawAll(gw);
84 BEAST_EXPECT(!amm.ammExists());
85 env.close();
86
87 // The AMM account does not exist at all now.
88 // It should return terNO_AMM error.
89 env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt),
90 ter(terNO_AMM));
91 }
92
93 // Test if the issuer field and holder field is the same. This should
94 // return temMALFORMED error.
95 {
96 Env env(*this, features);
97 Account gw{"gateway"};
98 Account alice{"alice"};
99 env.fund(XRP(10000), gw, alice);
100 env.close();
101
102 // gw sets asfAllowTrustLineClawback.
104 env.close();
106
107 // gw issues 100 USD to Alice.
108 auto const USD = gw["USD"];
109 env.trust(USD(1000), alice);
110 env(pay(gw, alice, USD(100)));
111 env.close();
112
113 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
114
115 // Issuer can not clawback from himself.
116 env(amm::ammClawback(gw, gw, USD, XRP, std::nullopt),
118
119 // Holder can not clawback from himself.
120 env(amm::ammClawback(alice, alice, USD, XRP, std::nullopt),
122 }
123
124 // Test if the Asset field matches the Account field.
125 {
126 Env env(*this, features);
127 Account gw{"gateway"};
128 Account alice{"alice"};
129 env.fund(XRP(10000), gw, alice);
130 env.close();
131
132 // gw sets asfAllowTrustLineClawback.
134 env.close();
136
137 // gw issues 100 USD to Alice.
138 auto const USD = gw["USD"];
139 env.trust(USD(1000), alice);
140 env(pay(gw, alice, USD(100)));
141 env.close();
142
143 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
144
145 // The Asset's issuer field is alice, while the Account field is gw.
146 // This should return temMALFORMED because they do not match.
148 gw,
149 alice,
150 Issue{gw["USD"].currency, alice.id()},
151 XRP,
152 std::nullopt),
154 }
155
156 // Test if the Amount field matches the Asset field.
157 {
158 Env env(*this, features);
159 Account gw{"gateway"};
160 Account alice{"alice"};
161 env.fund(XRP(10000), gw, alice);
162 env.close();
163
164 // gw sets asfAllowTrustLineClawback.
166 env.close();
168
169 // gw issues 100 USD to Alice.
170 auto const USD = gw["USD"];
171 env.trust(USD(1000), alice);
172 env(pay(gw, alice, USD(100)));
173 env.close();
174
175 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
176
177 // The Asset's issuer subfield is gw account and Amount's issuer
178 // subfield is alice account. Return temBAD_AMOUNT because
179 // they do not match.
181 gw,
182 alice,
183 USD,
184 XRP,
185 STAmount{Issue{gw["USD"].currency, alice.id()}, 1}),
187 }
188
189 // Test if the Amount is invalid, which is less than zero.
190 {
191 Env env(*this, features);
192 Account gw{"gateway"};
193 Account alice{"alice"};
194 env.fund(XRP(10000), gw, alice);
195 env.close();
196
197 // gw sets asfAllowTrustLineClawback.
199 env.close();
201
202 // gw issues 100 USD to Alice.
203 auto const USD = gw["USD"];
204 env.trust(USD(1000), alice);
205 env(pay(gw, alice, USD(100)));
206 env.close();
207
208 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
209
210 // Return temBAD_AMOUNT if the Amount value is less than 0.
212 gw,
213 alice,
214 USD,
215 XRP,
216 STAmount{Issue{gw["USD"].currency, gw.id()}, -1}),
218
219 // Return temBAD_AMOUNT if the Amount value is 0.
221 gw,
222 alice,
223 USD,
224 XRP,
225 STAmount{Issue{gw["USD"].currency, gw.id()}, 0}),
227 }
228
229 // Test if the issuer did not set asfAllowTrustLineClawback, AMMClawback
230 // transaction is prohibited.
231 {
232 Env env(*this, features);
233 Account gw{"gateway"};
234 Account alice{"alice"};
235 env.fund(XRP(10000), gw, alice);
236 env.close();
237
238 // gw issues 100 USD to Alice.
239 auto const USD = gw["USD"];
240 env.trust(USD(1000), alice);
241 env(pay(gw, alice, USD(100)));
242 env.close();
243 env.require(balance(alice, gw["USD"](100)));
244 env.require(balance(gw, alice["USD"](-100)));
245
246 // gw creates AMM pool of XRP/USD.
247 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
248
249 // If asfAllowTrustLineClawback is not set, the issuer is not
250 // allowed to send the AMMClawback transaction.
251 env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt),
253 }
254
255 // Test invalid flag.
256 {
257 Env env(*this, features);
258 Account gw{"gateway"};
259 Account alice{"alice"};
260 env.fund(XRP(10000), gw, alice);
261 env.close();
262
263 // gw sets asfAllowTrustLineClawback.
265 env.close();
267
268 // gw issues 100 USD to Alice.
269 auto const USD = gw["USD"];
270 env.trust(USD(1000), alice);
271 env(pay(gw, alice, USD(100)));
272 env.close();
273
274 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
275
276 // Return temINVALID_FLAG when providing invalid flag.
277 env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt),
280 }
281
282 // Test if tfClawTwoAssets is set when the two assets in the AMM pool
283 // are not issued by the same issuer.
284 {
285 Env env(*this, features);
286 Account gw{"gateway"};
287 Account alice{"alice"};
288 env.fund(XRP(10000), gw, alice);
289 env.close();
290
291 // gw sets asfAllowTrustLineClawback.
293 env.close();
295
296 // gw issues 100 USD to Alice.
297 auto const USD = gw["USD"];
298 env.trust(USD(1000), alice);
299 env(pay(gw, alice, USD(100)));
300 env.close();
301
302 // gw creates AMM pool of XRP/USD.
303 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
304
305 // Return temINVALID_FLAG because the issuer set tfClawTwoAssets,
306 // but the issuer only issues USD in the pool. The issuer is not
307 // allowed to set tfClawTwoAssets flag if he did not issue both
308 // assets in the pool.
309 env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt),
312 }
313
314 // Test clawing back XRP is being prohibited.
315 {
316 Env env(*this, features);
317 Account gw{"gateway"};
318 Account alice{"alice"};
319 env.fund(XRP(1000000), gw, alice);
320 env.close();
321
322 // gw sets asfAllowTrustLineClawback.
324 env.close();
326
327 // gw issues 3000 USD to Alice.
328 auto const USD = gw["USD"];
329 env.trust(USD(100000), alice);
330 env(pay(gw, alice, USD(3000)));
331 env.close();
332
333 // Alice creates AMM pool of XRP/USD.
334 AMM amm(env, alice, XRP(1000), USD(2000), ter(tesSUCCESS));
335 env.close();
336
337 // Clawback XRP is prohibited.
338 env(amm::ammClawback(gw, alice, XRP, USD, std::nullopt),
340 }
341 }
342
343 void
345 {
346 testcase("test featureAMMClawback is not enabled.");
347 using namespace jtx;
348 if (!features[featureAMMClawback])
349 {
350 Env env(*this, features);
351 Account gw{"gateway"};
352 Account alice{"alice"};
353 env.fund(XRP(1000000), gw, alice);
354 env.close();
355
356 // gw sets asfAllowTrustLineClawback.
358 env.close();
360
361 // gw issues 3000 USD to Alice.
362 auto const USD = gw["USD"];
363 env.trust(USD(100000), alice);
364 env(pay(gw, alice, USD(3000)));
365 env.close();
366
367 // When featureAMMClawback is not enabled, AMMClawback is disabled.
368 // Because when featureAMMClawback is disabled, we can not create
369 // amm account, call amm::ammClawback directly for testing purpose.
370 env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt),
372 }
373 }
374
375 void
377 {
378 testcase("test AMMClawback specific amount");
379 using namespace jtx;
380
381 // Test AMMClawback for USD/EUR pool. The assets are issued by different
382 // issuer. Claw back USD, and EUR goes back to the holder.
383 {
384 Env env(*this, features);
385 Account gw{"gateway"};
386 Account gw2{"gateway2"};
387 Account alice{"alice"};
388 env.fund(XRP(1000000), gw, gw2, alice);
389 env.close();
390
391 // gw sets asfAllowTrustLineClawback.
393 env.close();
395
396 // gw issues 3000 USD to Alice.
397 auto const USD = gw["USD"];
398 env.trust(USD(100000), alice);
399 env(pay(gw, alice, USD(3000)));
400 env.close();
401 env.require(balance(gw, alice["USD"](-3000)));
402 env.require(balance(alice, gw["USD"](3000)));
403
404 // gw2 issues 3000 EUR to Alice.
405 auto const EUR = gw2["EUR"];
406 env.trust(EUR(100000), alice);
407 env(pay(gw2, alice, EUR(3000)));
408 env.close();
409 env.require(balance(gw2, alice["EUR"](-3000)));
410 env.require(balance(alice, gw2["EUR"](3000)));
411
412 // Alice creates AMM pool of EUR/USD.
413 AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS));
414 env.close();
415
416 BEAST_EXPECT(amm.expectBalances(
417 USD(2000), EUR(1000), IOUAmount{1414213562373095, -12}));
418
419 // gw clawback 1000 USD from the AMM pool.
420 env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)),
421 ter(tesSUCCESS));
422 env.close();
423
424 // Alice's initial balance for USD is 3000 USD. Alice deposited 2000
425 // USD into the pool, then she has 1000 USD. And 1000 USD was clawed
426 // back from the AMM pool, so she still has 1000 USD.
427 env.require(balance(gw, alice["USD"](-1000)));
428 env.require(balance(alice, gw["USD"](1000)));
429
430 // Alice's initial balance for EUR is 3000 EUR. Alice deposited 1000
431 // EUR into the pool, 500 EUR was withdrawn proportionally. So she
432 // has 2500 EUR now.
433 env.require(balance(gw2, alice["EUR"](-2500)));
434 env.require(balance(alice, gw2["EUR"](2500)));
435
436 // 1000 USD and 500 EUR was withdrawn from the AMM pool, so the
437 // current balance is 1000 USD and 500 EUR.
438 BEAST_EXPECT(amm.expectBalances(
439 USD(1000), EUR(500), IOUAmount{7071067811865475, -13}));
440
441 // Alice has half of its initial lptokens Left.
442 BEAST_EXPECT(
443 amm.expectLPTokens(alice, IOUAmount{7071067811865475, -13}));
444
445 // gw clawback another 1000 USD from the AMM pool. The AMM pool will
446 // be empty and get deleted.
447 env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)),
448 ter(tesSUCCESS));
449 env.close();
450
451 // Alice should still has 1000 USD because gw clawed back from the
452 // AMM pool.
453 env.require(balance(gw, alice["USD"](-1000)));
454 env.require(balance(alice, gw["USD"](1000)));
455
456 // Alice should has 3000 EUR now because another 500 EUR was
457 // withdrawn.
458 env.require(balance(gw2, alice["EUR"](-3000)));
459 env.require(balance(alice, gw2["EUR"](3000)));
460
461 // amm is automatically deleted.
462 BEAST_EXPECT(!amm.ammExists());
463 }
464
465 // Test AMMClawback for USD/XRP pool. Claw back USD, and XRP goes back
466 // to the holder.
467 {
468 Env env(*this, features);
469 Account gw{"gateway"};
470 Account alice{"alice"};
471 env.fund(XRP(1000000), gw, alice);
472 env.close();
473
474 // gw sets asfAllowTrustLineClawback.
476 env.close();
478
479 // gw issues 3000 USD to Alice.
480 auto const USD = gw["USD"];
481 env.trust(USD(100000), alice);
482 env(pay(gw, alice, USD(3000)));
483 env.close();
484 env.require(balance(gw, alice["USD"](-3000)));
485 env.require(balance(alice, gw["USD"](3000)));
486
487 // Alice creates AMM pool of XRP/USD.
488 AMM amm(env, alice, XRP(1000), USD(2000), ter(tesSUCCESS));
489 env.close();
490
491 BEAST_EXPECT(amm.expectBalances(
492 USD(2000), XRP(1000), IOUAmount{1414213562373095, -9}));
493
494 auto aliceXrpBalance = env.balance(alice, XRP);
495
496 // gw clawback 1000 USD from the AMM pool.
497 env(amm::ammClawback(gw, alice, USD, XRP, USD(1000)),
498 ter(tesSUCCESS));
499 env.close();
500
501 // Alice's initial balance for USD is 3000 USD. Alice deposited 2000
502 // USD into the pool, then she has 1000 USD. And 1000 USD was clawed
503 // back from the AMM pool, so she still has 1000 USD.
504 env.require(balance(gw, alice["USD"](-1000)));
505 env.require(balance(alice, gw["USD"](1000)));
506
507 // Alice will get 500 XRP back.
508 BEAST_EXPECT(
509 expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(500)));
510
511 // 1000 USD and 500 XRP was withdrawn from the AMM pool, so the
512 // current balance is 1000 USD and 500 XRP.
513 BEAST_EXPECT(amm.expectBalances(
514 USD(1000), XRP(500), IOUAmount{7071067811865475, -10}));
515
516 // Alice has half of its initial lptokens Left.
517 BEAST_EXPECT(
518 amm.expectLPTokens(alice, IOUAmount{7071067811865475, -10}));
519
520 // gw clawback another 1000 USD from the AMM pool. The AMM pool will
521 // be empty and get deleted.
522 env(amm::ammClawback(gw, alice, USD, XRP, USD(1000)),
523 ter(tesSUCCESS));
524 env.close();
525
526 // Alice should still has 1000 USD because gw clawed back from the
527 // AMM pool.
528 env.require(balance(gw, alice["USD"](-1000)));
529 env.require(balance(alice, gw["USD"](1000)));
530
531 // Alice will get another 1000 XRP back.
532 BEAST_EXPECT(
533 expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000)));
534
535 // amm is automatically deleted.
536 BEAST_EXPECT(!amm.ammExists());
537 }
538 }
539
540 void
542 {
543 testcase(
544 "test AMMClawback specific amount which exceeds the current "
545 "balance");
546 using namespace jtx;
547
548 // Test AMMClawback for USD/EUR pool. The assets are issued by different
549 // issuer. Claw back USD for multiple times, and EUR goes back to the
550 // holder. The last AMMClawback transaction exceeds the holder's USD
551 // balance in AMM pool.
552 {
553 Env env(*this, features);
554 Account gw{"gateway"};
555 Account gw2{"gateway2"};
556 Account alice{"alice"};
557 env.fund(XRP(1000000), gw, gw2, alice);
558 env.close();
559
560 // gw sets asfAllowTrustLineClawback.
562 env.close();
564
565 // gw issues 6000 USD to Alice.
566 auto const USD = gw["USD"];
567 env.trust(USD(100000), alice);
568 env(pay(gw, alice, USD(6000)));
569 env.close();
570 env.require(balance(alice, gw["USD"](6000)));
571
572 // gw2 issues 6000 EUR to Alice.
573 auto const EUR = gw2["EUR"];
574 env.trust(EUR(100000), alice);
575 env(pay(gw2, alice, EUR(6000)));
576 env.close();
577 env.require(balance(alice, gw2["EUR"](6000)));
578
579 // Alice creates AMM pool of EUR/USD
580 AMM amm(env, alice, EUR(5000), USD(4000), ter(tesSUCCESS));
581 env.close();
582
583 BEAST_EXPECT(amm.expectBalances(
584 USD(4000), EUR(5000), IOUAmount{4472135954999580, -12}));
585
586 // gw clawback 1000 USD from the AMM pool
587 env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)),
588 ter(tesSUCCESS));
589 env.close();
590
591 // Alice's initial balance for USD is 6000 USD. Alice deposited 4000
592 // USD into the pool, then she has 2000 USD. And 1000 USD was clawed
593 // back from the AMM pool, so she still has 2000 USD.
594 env.require(balance(alice, gw["USD"](2000)));
595
596 // Alice's initial balance for EUR is 6000 EUR. Alice deposited 5000
597 // EUR into the pool, 1250 EUR was withdrawn proportionally. So she
598 // has 2500 EUR now.
599 env.require(balance(alice, gw2["EUR"](2250)));
600
601 // 1000 USD and 1250 EUR was withdrawn from the AMM pool, so the
602 // current balance is 3000 USD and 3750 EUR.
603 BEAST_EXPECT(amm.expectBalances(
604 USD(3000), EUR(3750), IOUAmount{3354101966249685, -12}));
605
606 // Alice has 3/4 of its initial lptokens Left.
607 BEAST_EXPECT(
608 amm.expectLPTokens(alice, IOUAmount{3354101966249685, -12}));
609
610 // gw clawback another 500 USD from the AMM pool.
611 env(amm::ammClawback(gw, alice, USD, EUR, USD(500)),
612 ter(tesSUCCESS));
613 env.close();
614
615 // Alice should still has 2000 USD because gw clawed back from the
616 // AMM pool.
617 env.require(balance(alice, gw["USD"](2000)));
618
619 BEAST_EXPECT(amm.expectBalances(
620 STAmount{USD, UINT64_C(2500000000000001), -12},
621 STAmount{EUR, UINT64_C(3125000000000001), -12},
622 IOUAmount{2795084971874738, -12}));
623
624 BEAST_EXPECT(
625 env.balance(alice, EUR) ==
626 STAmount(EUR, UINT64_C(2874999999999999), -12));
627
628 // gw clawback small amount, 1 USD.
630 env.close();
631
632 // Another 1 USD / 1.25 EUR was withdrawn.
633 env.require(balance(alice, gw["USD"](2000)));
634
635 BEAST_EXPECT(amm.expectBalances(
636 STAmount{USD, UINT64_C(2499000000000002), -12},
637 STAmount{EUR, UINT64_C(3123750000000002), -12},
638 IOUAmount{2793966937885989, -12}));
639
640 BEAST_EXPECT(
641 env.balance(alice, EUR) ==
642 STAmount(EUR, UINT64_C(2876249999999998), -12));
643
644 // gw clawback 4000 USD, exceeding the current balance. We
645 // will clawback all.
646 env(amm::ammClawback(gw, alice, USD, EUR, USD(4000)),
647 ter(tesSUCCESS));
648 env.close();
649
650 env.require(balance(alice, gw["USD"](2000)));
651
652 // All alice's EUR in the pool goes back to alice.
653 BEAST_EXPECT(
654 env.balance(alice, EUR) ==
655 STAmount(EUR, UINT64_C(6000000000000000), -12));
656
657 // amm is automatically deleted.
658 BEAST_EXPECT(!amm.ammExists());
659 }
660
661 // Test AMMClawback for USD/XRP pool. Claw back USD for multiple times,
662 // and XRP goes back to the holder. The last AMMClawback transaction
663 // exceeds the holder's USD balance in AMM pool. In this case, gw
664 // creates the AMM pool USD/XRP, both alice and bob deposit into it. gw2
665 // creates the AMM pool EUR/XRP.
666 {
667 Env env(*this, features);
668 Account gw{"gateway"};
669 Account gw2{"gateway2"};
670 Account alice{"alice"};
671 Account bob{"bob"};
672 env.fund(XRP(1000000), gw, gw2, alice, bob);
673 env.close();
674
675 // gw sets asfAllowTrustLineClawback.
677 env.close();
679
680 // gw2 sets asfAllowTrustLineClawback.
682 env.close();
684
685 // gw issues 6000 USD to Alice and 5000 USD to Bob.
686 auto const USD = gw["USD"];
687 env.trust(USD(100000), alice);
688 env(pay(gw, alice, USD(6000)));
689 env.trust(USD(100000), bob);
690 env(pay(gw, bob, USD(5000)));
691 env.close();
692
693 // gw2 issues 5000 EUR to Alice and 4000 EUR to Bob.
694 auto const EUR = gw2["EUR"];
695 env.trust(EUR(100000), alice);
696 env(pay(gw2, alice, EUR(5000)));
697 env.trust(EUR(100000), bob);
698 env(pay(gw2, bob, EUR(4000)));
699 env.close();
700
701 // gw creates AMM pool of XRP/USD, alice and bob deposit XRP/USD.
702 AMM amm(env, gw, XRP(2000), USD(1000), ter(tesSUCCESS));
703 BEAST_EXPECT(amm.expectBalances(
704 USD(1000), XRP(2000), IOUAmount{1414213562373095, -9}));
705 amm.deposit(alice, USD(1000), XRP(2000));
706 BEAST_EXPECT(amm.expectBalances(
707 USD(2000), XRP(4000), IOUAmount{2828427124746190, -9}));
708 amm.deposit(bob, USD(1000), XRP(2000));
709 BEAST_EXPECT(amm.expectBalances(
710 USD(3000), XRP(6000), IOUAmount{4242640687119285, -9}));
711 env.close();
712
713 // gw2 creates AMM pool of XRP/EUR, alice and bob deposit XRP/EUR.
714 AMM amm2(env, gw2, XRP(3000), EUR(1000), ter(tesSUCCESS));
715 BEAST_EXPECT(amm2.expectBalances(
716 EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9}));
717 amm2.deposit(alice, EUR(1000), XRP(3000));
718 BEAST_EXPECT(amm2.expectBalances(
719 EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9}));
720 amm2.deposit(bob, EUR(1000), XRP(3000));
721 BEAST_EXPECT(amm2.expectBalances(
722 EUR(3000), XRP(9000), IOUAmount{5196152422706634, -9}));
723 env.close();
724
725 auto aliceXrpBalance = env.balance(alice, XRP);
726 auto bobXrpBalance = env.balance(bob, XRP);
727
728 // gw clawback 500 USD from alice in amm
729 env(amm::ammClawback(gw, alice, USD, XRP, USD(500)),
730 ter(tesSUCCESS));
731 env.close();
732
733 // Alice's initial balance for USD is 6000 USD. Alice deposited 1000
734 // USD into the pool, then she has 5000 USD. And 500 USD was clawed
735 // back from the AMM pool, so she still has 5000 USD.
736 env.require(balance(alice, gw["USD"](5000)));
737
738 // Bob's balance is not changed.
739 env.require(balance(bob, gw["USD"](4000)));
740
741 // Alice gets 1000 XRP back.
742 BEAST_EXPECT(
743 expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(1000)));
744
745 BEAST_EXPECT(amm.expectBalances(
746 USD(2500), XRP(5000), IOUAmount{3535533905932738, -9}));
747 BEAST_EXPECT(
748 amm.expectLPTokens(alice, IOUAmount{7071067811865480, -10}));
749 BEAST_EXPECT(
750 amm.expectLPTokens(bob, IOUAmount{1414213562373095, -9}));
751
752 // gw clawback 10 USD from bob in amm.
753 env(amm::ammClawback(gw, bob, USD, XRP, USD(10)), ter(tesSUCCESS));
754 env.close();
755
756 env.require(balance(alice, gw["USD"](5000)));
757 env.require(balance(bob, gw["USD"](4000)));
758
759 // Bob gets 20 XRP back.
760 BEAST_EXPECT(
761 expectLedgerEntryRoot(env, bob, bobXrpBalance + XRP(20)));
762 BEAST_EXPECT(amm.expectBalances(
763 STAmount{USD, UINT64_C(2490000000000001), -12},
764 XRP(4980),
765 IOUAmount{3521391770309008, -9}));
766 BEAST_EXPECT(
767 amm.expectLPTokens(alice, IOUAmount{7071067811865480, -10}));
768 BEAST_EXPECT(
769 amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9}));
770
771 // gw2 clawback 200 EUR from amm2.
772 env(amm::ammClawback(gw2, alice, EUR, XRP, EUR(200)),
773 ter(tesSUCCESS));
774 env.close();
775
776 env.require(balance(alice, gw2["EUR"](4000)));
777 env.require(balance(bob, gw2["EUR"](3000)));
778
779 // Alice gets 600 XRP back.
780 BEAST_EXPECT(expectLedgerEntryRoot(
781 env, alice, aliceXrpBalance + XRP(1000) + XRP(600)));
782 BEAST_EXPECT(amm2.expectBalances(
783 EUR(2800), XRP(8400), IOUAmount{4849742261192859, -9}));
784 BEAST_EXPECT(
785 amm2.expectLPTokens(alice, IOUAmount{1385640646055103, -9}));
786 BEAST_EXPECT(
787 amm2.expectLPTokens(bob, IOUAmount{1732050807568878, -9}));
788
789 // gw claw back 1000 USD from alice in amm, which exceeds alice's
790 // balance. This will clawback all the remaining LP tokens of alice
791 // (corresponding 500 USD / 1000 XRP).
792 env(amm::ammClawback(gw, alice, USD, XRP, USD(1000)),
793 ter(tesSUCCESS));
794 env.close();
795
796 env.require(balance(alice, gw["USD"](5000)));
797 env.require(balance(bob, gw["USD"](4000)));
798
799 // Alice gets 1000 XRP back.
800 BEAST_EXPECT(expectLedgerEntryRoot(
801 env,
802 alice,
803 aliceXrpBalance + XRP(1000) + XRP(600) + XRP(1000)));
804 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0)));
805 BEAST_EXPECT(
806 amm.expectLPTokens(bob, IOUAmount{1400071426749365, -9}));
807 BEAST_EXPECT(amm.expectBalances(
808 STAmount{USD, UINT64_C(1990000000000001), -12},
809 XRP(3980),
810 IOUAmount{2814284989122460, -9}));
811
812 // gw clawback 1000 USD from bob in amm, which also exceeds bob's
813 // balance in amm. All bob's lptoken in amm will be consumed, which
814 // corresponds to 990 USD / 1980 XRP
815 env(amm::ammClawback(gw, bob, USD, XRP, USD(1000)),
816 ter(tesSUCCESS));
817 env.close();
818
819 env.require(balance(alice, gw["USD"](5000)));
820 env.require(balance(bob, gw["USD"](4000)));
821
822 BEAST_EXPECT(expectLedgerEntryRoot(
823 env,
824 alice,
825 aliceXrpBalance + XRP(1000) + XRP(600) + XRP(1000)));
826 BEAST_EXPECT(expectLedgerEntryRoot(
827 env, bob, bobXrpBalance + XRP(20) + XRP(1980)));
828
829 // Now neither alice nor bob has any lptoken in amm.
830 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0)));
831 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0)));
832
833 // gw2 claw back 1000 EUR from alice in amm2, which exceeds alice's
834 // balance. All alice's lptokens will be consumed, which corresponds
835 // to 800EUR / 2400 XRP.
836 env(amm::ammClawback(gw2, alice, EUR, XRP, EUR(1000)),
837 ter(tesSUCCESS));
838 env.close();
839
840 env.require(balance(alice, gw2["EUR"](4000)));
841 env.require(balance(bob, gw2["EUR"](3000)));
842
843 // Alice gets another 2400 XRP back, bob's XRP balance remains the
844 // same.
845 BEAST_EXPECT(expectLedgerEntryRoot(
846 env,
847 alice,
848 aliceXrpBalance + XRP(1000) + XRP(600) + XRP(1000) +
849 XRP(2400)));
850 BEAST_EXPECT(expectLedgerEntryRoot(
851 env, bob, bobXrpBalance + XRP(20) + XRP(1980)));
852
853 // Alice now does not have any lptoken in amm2
854 BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount(0)));
855
856 BEAST_EXPECT(amm2.expectBalances(
857 EUR(2000), XRP(6000), IOUAmount{3464101615137756, -9}));
858
859 // gw2 claw back 2000 EUR from bib in amm2, which exceeds bob's
860 // balance. All bob's lptokens will be consumed, which corresponds
861 // to 1000EUR / 3000 XRP.
862 env(amm::ammClawback(gw2, bob, EUR, XRP, EUR(2000)),
863 ter(tesSUCCESS));
864 env.close();
865
866 env.require(balance(alice, gw2["EUR"](4000)));
867 env.require(balance(bob, gw2["EUR"](3000)));
868
869 // Bob gets another 3000 XRP back. Alice's XRP balance remains the
870 // same.
871 BEAST_EXPECT(expectLedgerEntryRoot(
872 env,
873 alice,
874 aliceXrpBalance + XRP(1000) + XRP(600) + XRP(1000) +
875 XRP(2400)));
876 BEAST_EXPECT(expectLedgerEntryRoot(
877 env, bob, bobXrpBalance + XRP(20) + XRP(1980) + XRP(3000)));
878
879 // Neither alice nor bob has any lptoken in amm2
880 BEAST_EXPECT(amm2.expectLPTokens(alice, IOUAmount(0)));
881 BEAST_EXPECT(amm2.expectLPTokens(bob, IOUAmount(0)));
882
883 BEAST_EXPECT(amm2.expectBalances(
884 EUR(1000), XRP(3000), IOUAmount{1732050807568878, -9}));
885 }
886 }
887
888 void
890 {
891 testcase("test AMMClawback all the tokens in the AMM pool");
892 using namespace jtx;
893
894 // Test AMMClawback for USD/EUR pool. The assets are issued by different
895 // issuer. Claw back all the USD for different users.
896 {
897 Env env(*this, features);
898 Account gw{"gateway"};
899 Account gw2{"gateway2"};
900 Account alice{"alice"};
901 Account bob{"bob"};
902 Account carol{"carol"};
903 env.fund(XRP(1000000), gw, gw2, alice, bob, carol);
904 env.close();
905
906 // gw sets asfAllowTrustLineClawback.
908 env.close();
910
911 // gw2 sets asfAllowTrustLineClawback.
913 env.close();
915
916 // gw issues 6000 USD to Alice, 5000 USD to Bob, and 4000 USD
917 // to Carol.
918 auto const USD = gw["USD"];
919 env.trust(USD(100000), alice);
920 env(pay(gw, alice, USD(6000)));
921 env.trust(USD(100000), bob);
922 env(pay(gw, bob, USD(5000)));
923 env.trust(USD(100000), carol);
924 env(pay(gw, carol, USD(4000)));
925 env.close();
926
927 // gw2 issues 6000 EUR to Alice and 5000 EUR to Bob and 4000
928 // EUR to Carol.
929 auto const EUR = gw2["EUR"];
930 env.trust(EUR(100000), alice);
931 env(pay(gw2, alice, EUR(6000)));
932 env.trust(EUR(100000), bob);
933 env(pay(gw2, bob, EUR(5000)));
934 env.trust(EUR(100000), carol);
935 env(pay(gw2, carol, EUR(4000)));
936 env.close();
937
938 // Alice creates AMM pool of EUR/USD
939 AMM amm(env, alice, EUR(5000), USD(4000), ter(tesSUCCESS));
940 env.close();
941
942 BEAST_EXPECT(amm.expectBalances(
943 USD(4000), EUR(5000), IOUAmount{4472135954999580, -12}));
944 amm.deposit(bob, USD(2000), EUR(2500));
945 BEAST_EXPECT(amm.expectBalances(
946 USD(6000), EUR(7500), IOUAmount{6708203932499370, -12}));
947 amm.deposit(carol, USD(1000), EUR(1250));
948 BEAST_EXPECT(amm.expectBalances(
949 USD(7000), EUR(8750), IOUAmount{7826237921249265, -12}));
950
951 BEAST_EXPECT(
952 amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12}));
953 BEAST_EXPECT(
954 amm.expectLPTokens(bob, IOUAmount{2236067977499790, -12}));
955 BEAST_EXPECT(
956 amm.expectLPTokens(carol, IOUAmount{1118033988749895, -12}));
957
958 env.require(balance(alice, gw["USD"](2000)));
959 env.require(balance(alice, gw2["EUR"](1000)));
960 env.require(balance(bob, gw["USD"](3000)));
961 env.require(balance(bob, gw2["EUR"](2500)));
962 env.require(balance(carol, gw["USD"](3000)));
963 env.require(balance(carol, gw2["EUR"](2750)));
964
965 // gw clawback all the bob's USD in amm. (2000 USD / 2500 EUR)
966 env(amm::ammClawback(gw, bob, USD, EUR, std::nullopt),
967 ter(tesSUCCESS));
968 env.close();
969
970 BEAST_EXPECT(amm.expectBalances(
971 STAmount{USD, UINT64_C(4999999999999999), -12},
972 STAmount{EUR, UINT64_C(6249999999999999), -12},
973 IOUAmount{5590169943749475, -12}));
974
975 BEAST_EXPECT(
976 amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12}));
977 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0)));
978 BEAST_EXPECT(
979 amm.expectLPTokens(carol, IOUAmount{1118033988749895, -12}));
980
981 // Bob will get 2500 EUR back.
982 env.require(balance(alice, gw["USD"](2000)));
983 env.require(balance(alice, gw2["EUR"](1000)));
984 BEAST_EXPECT(
985 env.balance(bob, USD) ==
986 STAmount(USD, UINT64_C(3000000000000000), -12));
987
988 BEAST_EXPECT(
989 env.balance(bob, EUR) ==
990 STAmount(EUR, UINT64_C(5000000000000001), -12));
991 env.require(balance(carol, gw["USD"](3000)));
992 env.require(balance(carol, gw2["EUR"](2750)));
993
994 // gw2 clawback all carol's EUR in amm. (1000 USD / 1250 EUR)
995 env(amm::ammClawback(gw2, carol, EUR, USD, std::nullopt),
996 ter(tesSUCCESS));
997 env.close();
998 BEAST_EXPECT(amm.expectBalances(
999 STAmount{USD, UINT64_C(3999999999999999), -12},
1000 STAmount{EUR, UINT64_C(4999999999999999), -12},
1001 IOUAmount{4472135954999580, -12}));
1002
1003 BEAST_EXPECT(
1004 amm.expectLPTokens(alice, IOUAmount{4472135954999580, -12}));
1005 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0)));
1006 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(0)));
1007
1008 // gw2 clawback all alice's EUR in amm. (4000 USD / 5000 EUR)
1009 env(amm::ammClawback(gw2, alice, EUR, USD, std::nullopt),
1010 ter(tesSUCCESS));
1011 env.close();
1012
1013 env.require(balance(carol, gw2["EUR"](2750)));
1014 env.require(balance(carol, gw["USD"](4000)));
1015 BEAST_EXPECT(!amm.ammExists());
1016 }
1017
1018 // Test AMMClawback for USD/XRP pool. Claw back all the USD for
1019 // different users.
1020 {
1021 Env env(*this, features);
1022 Account gw{"gateway"};
1023 Account alice{"alice"};
1024 Account bob{"bob"};
1025 env.fund(XRP(1000000), gw, alice, bob);
1026 env.close();
1027
1028 // gw sets asfAllowTrustLineClawback
1030 env.close();
1032
1033 // gw issues 600000 USD to Alice and 500000 USD to Bob.
1034 auto const USD = gw["USD"];
1035 env.trust(USD(1000000), alice);
1036 env(pay(gw, alice, USD(600000)));
1037 env.trust(USD(1000000), bob);
1038 env(pay(gw, bob, USD(500000)));
1039 env.close();
1040
1041 // gw creates AMM pool of XRP/USD, alice and bob deposit XRP/USD.
1042 AMM amm(env, gw, XRP(2000), USD(10000), ter(tesSUCCESS));
1043 BEAST_EXPECT(amm.expectBalances(
1044 USD(10000), XRP(2000), IOUAmount{4472135954999580, -9}));
1045 amm.deposit(alice, USD(1000), XRP(200));
1046 BEAST_EXPECT(amm.expectBalances(
1047 USD(11000), XRP(2200), IOUAmount{4919349550499538, -9}));
1048 amm.deposit(bob, USD(2000), XRP(400));
1049 BEAST_EXPECT(amm.expectBalances(
1050 USD(13000), XRP(2600), IOUAmount{5813776741499453, -9}));
1051 env.close();
1052
1053 auto aliceXrpBalance = env.balance(alice, XRP);
1054 auto bobXrpBalance = env.balance(bob, XRP);
1055
1056 // gw clawback all alice's USD in amm. (1000 USD / 200 XRP)
1057 env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt),
1058 ter(tesSUCCESS));
1059 env.close();
1060 BEAST_EXPECT(amm.expectBalances(
1061 USD(12000), XRP(2400), IOUAmount{5366563145999495, -9}));
1062 BEAST_EXPECT(
1063 expectLedgerEntryRoot(env, alice, aliceXrpBalance + XRP(200)));
1064 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0)));
1065
1066 // gw clawback all bob's USD in amm. (2000 USD / 400 XRP)
1067 env(amm::ammClawback(gw, bob, USD, XRP, std::nullopt),
1068 ter(tesSUCCESS));
1069 env.close();
1070 BEAST_EXPECT(amm.expectBalances(
1071 USD(10000), XRP(2000), IOUAmount{4472135954999580, -9}));
1072 BEAST_EXPECT(
1073 expectLedgerEntryRoot(env, bob, bobXrpBalance + XRP(400)));
1074 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0)));
1075 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0)));
1076 }
1077 }
1078
1079 void
1081 {
1082 testcase(
1083 "test AMMClawback from AMM pool with assets having the same "
1084 "issuer");
1085 using namespace jtx;
1086
1087 // Test AMMClawback for USD/EUR pool. The assets are issued by different
1088 // issuer. Claw back all the USD for different users.
1089 Env env(*this, features);
1090 Account gw{"gateway"};
1091 Account alice{"alice"};
1092 Account bob{"bob"};
1093 Account carol{"carol"};
1094 env.fund(XRP(1000000), gw, alice, bob, carol);
1095 env.close();
1096
1097 // gw sets asfAllowTrustLineClawback.
1099 env.close();
1101
1102 auto const USD = gw["USD"];
1103 env.trust(USD(100000), alice);
1104 env(pay(gw, alice, USD(10000)));
1105 env.trust(USD(100000), bob);
1106 env(pay(gw, bob, USD(9000)));
1107 env.trust(USD(100000), carol);
1108 env(pay(gw, carol, USD(8000)));
1109 env.close();
1110
1111 auto const EUR = gw["EUR"];
1112 env.trust(EUR(100000), alice);
1113 env(pay(gw, alice, EUR(10000)));
1114 env.trust(EUR(100000), bob);
1115 env(pay(gw, bob, EUR(9000)));
1116 env.trust(EUR(100000), carol);
1117 env(pay(gw, carol, EUR(8000)));
1118 env.close();
1119
1120 AMM amm(env, alice, EUR(2000), USD(8000), ter(tesSUCCESS));
1121 env.close();
1122
1123 BEAST_EXPECT(amm.expectBalances(USD(8000), EUR(2000), IOUAmount(4000)));
1124 amm.deposit(bob, USD(4000), EUR(1000));
1125 BEAST_EXPECT(
1126 amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000)));
1127 amm.deposit(carol, USD(2000), EUR(500));
1128 BEAST_EXPECT(
1129 amm.expectBalances(USD(14000), EUR(3500), IOUAmount(7000)));
1130
1131 // gw clawback 1000 USD from carol.
1132 env(amm::ammClawback(gw, carol, USD, EUR, USD(1000)), ter(tesSUCCESS));
1133 env.close();
1134 BEAST_EXPECT(
1135 amm.expectBalances(USD(13000), EUR(3250), IOUAmount(6500)));
1136
1137 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(4000)));
1138 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(2000)));
1139 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(500)));
1140 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1141 BEAST_EXPECT(env.balance(alice, EUR) == EUR(8000));
1142 BEAST_EXPECT(env.balance(bob, USD) == USD(5000));
1143 BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000));
1144 BEAST_EXPECT(env.balance(carol, USD) == USD(6000));
1145 // 250 EUR goes back to carol.
1146 BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750));
1147
1148 // gw clawback 1000 USD from bob with tfClawTwoAssets flag.
1149 // then the corresponding EUR will also be clawed back
1150 // by gw.
1151 env(amm::ammClawback(gw, bob, USD, EUR, USD(1000)),
1153 ter(tesSUCCESS));
1154 env.close();
1155 BEAST_EXPECT(
1156 amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000)));
1157
1158 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(4000)));
1159 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(1500)));
1160 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(500)));
1161 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1162 BEAST_EXPECT(env.balance(alice, EUR) == EUR(8000));
1163 BEAST_EXPECT(env.balance(bob, USD) == USD(5000));
1164 // 250 EUR did not go back to bob because tfClawTwoAssets is set.
1165 BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000));
1166 BEAST_EXPECT(env.balance(carol, USD) == USD(6000));
1167 BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750));
1168
1169 // gw clawback all USD from alice and set tfClawTwoAssets.
1170 env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt),
1172 ter(tesSUCCESS));
1173 env.close();
1174 BEAST_EXPECT(amm.expectBalances(USD(4000), EUR(1000), IOUAmount(2000)));
1175
1176 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0)));
1177 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(1500)));
1178 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(500)));
1179 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1180 BEAST_EXPECT(env.balance(alice, EUR) == EUR(8000));
1181 BEAST_EXPECT(env.balance(bob, USD) == USD(5000));
1182 BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000));
1183 BEAST_EXPECT(env.balance(carol, USD) == USD(6000));
1184 BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750));
1185 }
1186
1187 void
1189 {
1190 testcase(
1191 "test AMMClawback from AMM pool with assets having the same "
1192 "currency, but from different issuer");
1193 using namespace jtx;
1194
1195 // Test AMMClawback for USD/EUR pool. The assets are issued by different
1196 // issuer. Claw back all the USD for different users.
1197 Env env(*this, features);
1198 Account gw{"gateway"};
1199 Account gw2{"gateway2"};
1200 Account alice{"alice"};
1201 Account bob{"bob"};
1202 env.fund(XRP(1000000), gw, gw2, alice, bob);
1203 env.close();
1204
1205 // gw sets asfAllowTrustLineClawback.
1207 env.close();
1209
1210 // gw2 sets asfAllowTrustLineClawback.
1212 env.close();
1214
1215 env.trust(gw["USD"](100000), alice);
1216 env(pay(gw, alice, gw["USD"](8000)));
1217 env.trust(gw["USD"](100000), bob);
1218 env(pay(gw, bob, gw["USD"](7000)));
1219
1220 env.trust(gw2["USD"](100000), alice);
1221 env(pay(gw2, alice, gw2["USD"](6000)));
1222 env.trust(gw2["USD"](100000), bob);
1223 env(pay(gw2, bob, gw2["USD"](5000)));
1224 env.close();
1225
1226 AMM amm(env, alice, gw["USD"](1000), gw2["USD"](1500), ter(tesSUCCESS));
1227 env.close();
1228
1229 BEAST_EXPECT(amm.expectBalances(
1230 gw["USD"](1000),
1231 gw2["USD"](1500),
1232 IOUAmount{1224744871391589, -12}));
1233 amm.deposit(bob, gw["USD"](2000), gw2["USD"](3000));
1234 BEAST_EXPECT(amm.expectBalances(
1235 gw["USD"](3000),
1236 gw2["USD"](4500),
1237 IOUAmount{3674234614174767, -12}));
1238
1239 // Issuer does not match with asset.
1240 env(amm::ammClawback(
1241 gw,
1242 alice,
1243 gw2["USD"],
1244 gw["USD"],
1245 STAmount{Issue{gw2["USD"].currency, gw2.id()}, 500}),
1246 ter(temMALFORMED));
1247
1248 // gw2 clawback 500 gw2[USD] from alice.
1249 env(amm::ammClawback(
1250 gw2,
1251 alice,
1252 gw2["USD"],
1253 gw["USD"],
1254 STAmount{Issue{gw2["USD"].currency, gw2.id()}, 500}),
1255 ter(tesSUCCESS));
1256 env.close();
1257 BEAST_EXPECT(amm.expectBalances(
1258 STAmount{gw["USD"], UINT64_C(2666666666666667), -12},
1259 gw2["USD"](4000),
1260 IOUAmount{3265986323710904, -12}));
1261
1262 BEAST_EXPECT(
1263 amm.expectLPTokens(alice, IOUAmount{8164965809277260, -13}));
1264 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount{2449489742783178, -12}));
1265 BEAST_EXPECT(
1266 env.balance(alice, gw["USD"]) ==
1267 STAmount(gw["USD"], UINT64_C(7333333333333333), -12));
1268 BEAST_EXPECT(env.balance(alice, gw2["USD"]) == gw2["USD"](4500));
1269 BEAST_EXPECT(env.balance(bob, gw["USD"]) == gw["USD"](5000));
1270 BEAST_EXPECT(env.balance(bob, gw2["USD"]) == gw2["USD"](2000));
1271
1272 // gw clawback all gw["USD"] from bob.
1273 env(amm::ammClawback(gw, bob, gw["USD"], gw2["USD"], std::nullopt),
1274 ter(tesSUCCESS));
1275 env.close();
1276 BEAST_EXPECT(amm.expectBalances(
1277 STAmount{gw["USD"], UINT64_C(6666666666666670), -13},
1278 gw2["USD"](1000),
1279 IOUAmount{8164965809277260, -13}));
1280
1281 BEAST_EXPECT(
1282 amm.expectLPTokens(alice, IOUAmount{8164965809277260, -13}));
1283 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(0)));
1284 BEAST_EXPECT(
1285 env.balance(alice, gw["USD"]) ==
1286 STAmount(gw["USD"], UINT64_C(7333333333333333), -12));
1287 BEAST_EXPECT(env.balance(alice, gw2["USD"]) == gw2["USD"](4500));
1288 BEAST_EXPECT(env.balance(bob, gw["USD"]) == gw["USD"](5000));
1289 // Bob gets 3000 gw2["USD"] back and now his balance is 5000.
1290 BEAST_EXPECT(env.balance(bob, gw2["USD"]) == gw2["USD"](5000));
1291 }
1292
1293 void
1295 {
1296 testcase("test AMMClawback when issuing token for each other");
1297 using namespace jtx;
1298
1299 // gw and gw2 issues token for each other. Test AMMClawback from
1300 // each other.
1301 Env env(*this, features);
1302 Account gw{"gateway"};
1303 Account gw2{"gateway2"};
1304 Account alice{"alice"};
1305 env.fund(XRP(1000000), gw, gw2, alice);
1306 env.close();
1307
1308 // gw sets asfAllowTrustLineClawback.
1310 env.close();
1312
1313 // gw2 sets asfAllowTrustLineClawback.
1315 env.close();
1317
1318 auto const USD = gw["USD"];
1319 env.trust(USD(100000), gw2);
1320 env(pay(gw, gw2, USD(5000)));
1321 env.trust(USD(100000), alice);
1322 env(pay(gw, alice, USD(5000)));
1323
1324 auto const EUR = gw2["EUR"];
1325 env.trust(EUR(100000), gw);
1326 env(pay(gw2, gw, EUR(6000)));
1327 env.trust(EUR(100000), alice);
1328 env(pay(gw2, alice, EUR(6000)));
1329 env.close();
1330
1331 AMM amm(env, gw, USD(1000), EUR(2000), ter(tesSUCCESS));
1332 env.close();
1333 BEAST_EXPECT(amm.expectBalances(
1334 USD(1000), EUR(2000), IOUAmount{1414213562373095, -12}));
1335
1336 amm.deposit(gw2, USD(2000), EUR(4000));
1337 BEAST_EXPECT(amm.expectBalances(
1338 USD(3000), EUR(6000), IOUAmount{4242640687119285, -12}));
1339
1340 amm.deposit(alice, USD(3000), EUR(6000));
1341 BEAST_EXPECT(amm.expectBalances(
1342 USD(6000), EUR(12000), IOUAmount{8485281374238570, -12}));
1343
1344 BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{1414213562373095, -12}));
1345 BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{2828427124746190, -12}));
1346 BEAST_EXPECT(
1347 amm.expectLPTokens(alice, IOUAmount{4242640687119285, -12}));
1348
1349 // gw claws back 1000 USD from gw2.
1350 env(amm::ammClawback(gw, gw2, USD, EUR, USD(1000)), ter(tesSUCCESS));
1351 env.close();
1352 BEAST_EXPECT(amm.expectBalances(
1353 USD(5000), EUR(10000), IOUAmount{7071067811865475, -12}));
1354
1355 BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{1414213562373095, -12}));
1356 BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12}));
1357 BEAST_EXPECT(
1358 amm.expectLPTokens(alice, IOUAmount{4242640687119285, -12}));
1359
1360 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1361 BEAST_EXPECT(env.balance(alice, EUR) == EUR(0));
1362 BEAST_EXPECT(env.balance(gw, EUR) == EUR(4000));
1363 BEAST_EXPECT(env.balance(gw2, USD) == USD(3000));
1364
1365 // gw2 claws back 1000 EUR from gw.
1366 env(amm::ammClawback(gw2, gw, EUR, USD, EUR(1000)), ter(tesSUCCESS));
1367 env.close();
1368 BEAST_EXPECT(amm.expectBalances(
1369 USD(4500),
1370 STAmount(EUR, UINT64_C(9000000000000001), -12),
1371 IOUAmount{6363961030678928, -12}));
1372
1373 BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13}));
1374 BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12}));
1375 BEAST_EXPECT(
1376 amm.expectLPTokens(alice, IOUAmount{4242640687119285, -12}));
1377
1378 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1379 BEAST_EXPECT(env.balance(alice, EUR) == EUR(0));
1380 BEAST_EXPECT(env.balance(gw, EUR) == EUR(4000));
1381 BEAST_EXPECT(env.balance(gw2, USD) == USD(3000));
1382
1383 // gw2 claws back 4000 EUR from alice.
1384 env(amm::ammClawback(gw2, alice, EUR, USD, EUR(4000)), ter(tesSUCCESS));
1385 env.close();
1386 BEAST_EXPECT(amm.expectBalances(
1387 USD(2500),
1388 STAmount(EUR, UINT64_C(5000000000000001), -12),
1389 IOUAmount{3535533905932738, -12}));
1390
1391 BEAST_EXPECT(amm.expectLPTokens(gw, IOUAmount{7071067811865480, -13}));
1392 BEAST_EXPECT(amm.expectLPTokens(gw2, IOUAmount{1414213562373095, -12}));
1393 BEAST_EXPECT(
1394 amm.expectLPTokens(alice, IOUAmount{1414213562373095, -12}));
1395
1396 BEAST_EXPECT(env.balance(alice, USD) == USD(4000));
1397 BEAST_EXPECT(env.balance(alice, EUR) == EUR(0));
1398 BEAST_EXPECT(env.balance(gw, EUR) == EUR(4000));
1399 BEAST_EXPECT(env.balance(gw2, USD) == USD(3000));
1400 }
1401
1402 void
1404 {
1405 testcase(
1406 "test AMMClawback from account which does not own any lptoken in "
1407 "the pool");
1408 using namespace jtx;
1409
1410 Env env(*this, features);
1411 Account gw{"gateway"};
1412 Account alice{"alice"};
1413 env.fund(XRP(1000000), gw, alice);
1414 env.close();
1415
1416 // gw sets asfAllowTrustLineClawback.
1418 env.close();
1420
1421 auto const USD = gw["USD"];
1422 env.trust(USD(100000), alice);
1423 env(pay(gw, alice, USD(5000)));
1424
1425 AMM amm(env, gw, USD(1000), XRP(2000), ter(tesSUCCESS));
1426 env.close();
1427
1428 // Alice did not deposit in the amm pool. So AMMClawback from Alice
1429 // will fail.
1430 env(amm::ammClawback(gw, alice, USD, XRP, USD(1000)),
1432 }
1433
1434 void
1436 {
1437 testcase("test assets frozen");
1438 using namespace jtx;
1439
1440 // test individually frozen trustline.
1441 {
1442 Env env(*this, features);
1443 Account gw{"gateway"};
1444 Account gw2{"gateway2"};
1445 Account alice{"alice"};
1446 env.fund(XRP(1000000), gw, gw2, alice);
1447 env.close();
1448
1449 // gw sets asfAllowTrustLineClawback.
1451 env.close();
1453
1454 // gw issues 3000 USD to Alice.
1455 auto const USD = gw["USD"];
1456 env.trust(USD(100000), alice);
1457 env(pay(gw, alice, USD(3000)));
1458 env.close();
1459 env.require(balance(alice, gw["USD"](3000)));
1460
1461 // gw2 issues 3000 EUR to Alice.
1462 auto const EUR = gw2["EUR"];
1463 env.trust(EUR(100000), alice);
1464 env(pay(gw2, alice, EUR(3000)));
1465 env.close();
1466 env.require(balance(alice, gw2["EUR"](3000)));
1467
1468 // Alice creates AMM pool of EUR/USD.
1469 AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS));
1470 env.close();
1471
1472 BEAST_EXPECT(amm.expectBalances(
1473 USD(2000), EUR(1000), IOUAmount{1414213562373095, -12}));
1474
1475 // freeze trustline
1476 env(trust(gw, alice["USD"](0), tfSetFreeze));
1477 env.close();
1478
1479 // gw clawback 1000 USD from the AMM pool.
1480 env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)),
1481 ter(tesSUCCESS));
1482 env.close();
1483
1484 env.require(balance(alice, gw["USD"](1000)));
1485 env.require(balance(alice, gw2["EUR"](2500)));
1486 BEAST_EXPECT(amm.expectBalances(
1487 USD(1000), EUR(500), IOUAmount{7071067811865475, -13}));
1488
1489 // Alice has half of its initial lptokens Left.
1490 BEAST_EXPECT(
1491 amm.expectLPTokens(alice, IOUAmount{7071067811865475, -13}));
1492
1493 // gw clawback another 1000 USD from the AMM pool. The AMM pool will
1494 // be empty and get deleted.
1495 env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)),
1496 ter(tesSUCCESS));
1497 env.close();
1498
1499 // Alice should still has 1000 USD because gw clawed back from the
1500 // AMM pool.
1501 env.require(balance(alice, gw["USD"](1000)));
1502 env.require(balance(alice, gw2["EUR"](3000)));
1503
1504 // amm is automatically deleted.
1505 BEAST_EXPECT(!amm.ammExists());
1506 }
1507
1508 // test individually frozen trustline of both USD and EUR currency.
1509 {
1510 Env env(*this, features);
1511 Account gw{"gateway"};
1512 Account gw2{"gateway2"};
1513 Account alice{"alice"};
1514 env.fund(XRP(1000000), gw, gw2, alice);
1515 env.close();
1516
1517 // gw sets asfAllowTrustLineClawback.
1519 env.close();
1521
1522 // gw issues 3000 USD to Alice.
1523 auto const USD = gw["USD"];
1524 env.trust(USD(100000), alice);
1525 env(pay(gw, alice, USD(3000)));
1526 env.close();
1527 env.require(balance(alice, gw["USD"](3000)));
1528
1529 // gw2 issues 3000 EUR to Alice.
1530 auto const EUR = gw2["EUR"];
1531 env.trust(EUR(100000), alice);
1532 env(pay(gw2, alice, EUR(3000)));
1533 env.close();
1534 env.require(balance(alice, gw2["EUR"](3000)));
1535
1536 // Alice creates AMM pool of EUR/USD.
1537 AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS));
1538 env.close();
1539
1540 BEAST_EXPECT(amm.expectBalances(
1541 USD(2000), EUR(1000), IOUAmount{1414213562373095, -12}));
1542
1543 // freeze trustlines
1544 env(trust(gw, alice["USD"](0), tfSetFreeze));
1545 env(trust(gw2, alice["EUR"](0), tfSetFreeze));
1546 env.close();
1547
1548 // gw clawback 1000 USD from the AMM pool.
1549 env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)),
1550 ter(tesSUCCESS));
1551 env.close();
1552
1553 env.require(balance(alice, gw["USD"](1000)));
1554 env.require(balance(alice, gw2["EUR"](2500)));
1555 BEAST_EXPECT(amm.expectBalances(
1556 USD(1000), EUR(500), IOUAmount{7071067811865475, -13}));
1557 BEAST_EXPECT(
1558 amm.expectLPTokens(alice, IOUAmount{7071067811865475, -13}));
1559 }
1560
1561 // test gw global freeze.
1562 {
1563 Env env(*this, features);
1564 Account gw{"gateway"};
1565 Account gw2{"gateway2"};
1566 Account alice{"alice"};
1567 env.fund(XRP(1000000), gw, gw2, alice);
1568 env.close();
1569
1570 // gw sets asfAllowTrustLineClawback.
1572 env.close();
1574
1575 // gw issues 3000 USD to Alice.
1576 auto const USD = gw["USD"];
1577 env.trust(USD(100000), alice);
1578 env(pay(gw, alice, USD(3000)));
1579 env.close();
1580 env.require(balance(alice, gw["USD"](3000)));
1581
1582 // gw2 issues 3000 EUR to Alice.
1583 auto const EUR = gw2["EUR"];
1584 env.trust(EUR(100000), alice);
1585 env(pay(gw2, alice, EUR(3000)));
1586 env.close();
1587 env.require(balance(alice, gw2["EUR"](3000)));
1588
1589 // Alice creates AMM pool of EUR/USD.
1590 AMM amm(env, alice, EUR(1000), USD(2000), ter(tesSUCCESS));
1591 env.close();
1592
1593 BEAST_EXPECT(amm.expectBalances(
1594 USD(2000), EUR(1000), IOUAmount{1414213562373095, -12}));
1595
1596 // global freeze
1597 env(fset(gw, asfGlobalFreeze));
1598 env.close();
1599
1600 // gw clawback 1000 USD from the AMM pool.
1601 env(amm::ammClawback(gw, alice, USD, EUR, USD(1000)),
1602 ter(tesSUCCESS));
1603 env.close();
1604
1605 env.require(balance(alice, gw["USD"](1000)));
1606 env.require(balance(alice, gw2["EUR"](2500)));
1607 BEAST_EXPECT(amm.expectBalances(
1608 USD(1000), EUR(500), IOUAmount{7071067811865475, -13}));
1609 BEAST_EXPECT(
1610 amm.expectLPTokens(alice, IOUAmount{7071067811865475, -13}));
1611 }
1612
1613 // Test both assets are issued by the same issuer. And issuer sets
1614 // global freeze.
1615 {
1616 Env env(*this, features);
1617 Account gw{"gateway"};
1618 Account alice{"alice"};
1619 Account bob{"bob"};
1620 Account carol{"carol"};
1621 env.fund(XRP(1000000), gw, alice, bob, carol);
1622 env.close();
1623
1624 // gw sets asfAllowTrustLineClawback.
1626 env.close();
1628
1629 auto const USD = gw["USD"];
1630 env.trust(USD(100000), alice);
1631 env(pay(gw, alice, USD(10000)));
1632 env.trust(USD(100000), bob);
1633 env(pay(gw, bob, USD(9000)));
1634 env.trust(USD(100000), carol);
1635 env(pay(gw, carol, USD(8000)));
1636 env.close();
1637
1638 auto const EUR = gw["EUR"];
1639 env.trust(EUR(100000), alice);
1640 env(pay(gw, alice, EUR(10000)));
1641 env.trust(EUR(100000), bob);
1642 env(pay(gw, bob, EUR(9000)));
1643 env.trust(EUR(100000), carol);
1644 env(pay(gw, carol, EUR(8000)));
1645 env.close();
1646
1647 AMM amm(env, alice, EUR(2000), USD(8000), ter(tesSUCCESS));
1648 env.close();
1649
1650 BEAST_EXPECT(
1651 amm.expectBalances(USD(8000), EUR(2000), IOUAmount(4000)));
1652 amm.deposit(bob, USD(4000), EUR(1000));
1653 BEAST_EXPECT(
1654 amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000)));
1655 amm.deposit(carol, USD(2000), EUR(500));
1656 BEAST_EXPECT(
1657 amm.expectBalances(USD(14000), EUR(3500), IOUAmount(7000)));
1658
1659 // global freeze
1660 env(fset(gw, asfGlobalFreeze));
1661 env.close();
1662
1663 // gw clawback 1000 USD from carol.
1664 env(amm::ammClawback(gw, carol, USD, EUR, USD(1000)),
1665 ter(tesSUCCESS));
1666 env.close();
1667 BEAST_EXPECT(
1668 amm.expectBalances(USD(13000), EUR(3250), IOUAmount(6500)));
1669
1670 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(4000)));
1671 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(2000)));
1672 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(500)));
1673 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1674 BEAST_EXPECT(env.balance(alice, EUR) == EUR(8000));
1675 BEAST_EXPECT(env.balance(bob, USD) == USD(5000));
1676 BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000));
1677 BEAST_EXPECT(env.balance(carol, USD) == USD(6000));
1678 // 250 EUR goes back to carol.
1679 BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750));
1680
1681 // gw clawback 1000 USD from bob with tfClawTwoAssets flag.
1682 // then the corresponding EUR will also be clawed back
1683 // by gw.
1684 env(amm::ammClawback(gw, bob, USD, EUR, USD(1000)),
1686 ter(tesSUCCESS));
1687 env.close();
1688 BEAST_EXPECT(
1689 amm.expectBalances(USD(12000), EUR(3000), IOUAmount(6000)));
1690
1691 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(4000)));
1692 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(1500)));
1693 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(500)));
1694 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1695 BEAST_EXPECT(env.balance(alice, EUR) == EUR(8000));
1696 BEAST_EXPECT(env.balance(bob, USD) == USD(5000));
1697 // 250 EUR did not go back to bob because tfClawTwoAssets is set.
1698 BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000));
1699 BEAST_EXPECT(env.balance(carol, USD) == USD(6000));
1700 BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750));
1701
1702 // gw clawback all USD from alice and set tfClawTwoAssets.
1703 env(amm::ammClawback(gw, alice, USD, EUR, std::nullopt),
1705 ter(tesSUCCESS));
1706 env.close();
1707 BEAST_EXPECT(
1708 amm.expectBalances(USD(4000), EUR(1000), IOUAmount(2000)));
1709
1710 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0)));
1711 BEAST_EXPECT(amm.expectLPTokens(bob, IOUAmount(1500)));
1712 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount(500)));
1713 BEAST_EXPECT(env.balance(alice, USD) == USD(2000));
1714 BEAST_EXPECT(env.balance(alice, EUR) == EUR(8000));
1715 BEAST_EXPECT(env.balance(bob, USD) == USD(5000));
1716 BEAST_EXPECT(env.balance(bob, EUR) == EUR(8000));
1717 BEAST_EXPECT(env.balance(carol, USD) == USD(6000));
1718 BEAST_EXPECT(env.balance(carol, EUR) == EUR(7750));
1719 }
1720 }
1721
1722 void
1724 {
1725 testcase("test single depoit and clawback");
1726 using namespace jtx;
1727
1728 // Test AMMClawback for USD/XRP pool. Claw back USD, and XRP goes back
1729 // to the holder.
1730 Env env(*this, features);
1731 Account gw{"gateway"};
1732 Account alice{"alice"};
1733 env.fund(XRP(1000000000), gw, alice);
1734 env.close();
1735
1736 // gw sets asfAllowTrustLineClawback.
1738 env.close();
1740
1741 // gw issues 1000 USD to Alice.
1742 auto const USD = gw["USD"];
1743 env.trust(USD(100000), alice);
1744 env(pay(gw, alice, USD(1000)));
1745 env.close();
1746 env.require(balance(alice, gw["USD"](1000)));
1747
1748 // gw creates AMM pool of XRP/USD.
1749 AMM amm(env, gw, XRP(100), USD(400), ter(tesSUCCESS));
1750 env.close();
1751
1752 BEAST_EXPECT(amm.expectBalances(USD(400), XRP(100), IOUAmount(200000)));
1753
1754 amm.deposit(alice, USD(400));
1755 env.close();
1756
1757 BEAST_EXPECT(amm.expectBalances(
1758 USD(800), XRP(100), IOUAmount{2828427124746190, -10}));
1759
1760 auto aliceXrpBalance = env.balance(alice, XRP);
1761
1762 env(amm::ammClawback(gw, alice, USD, XRP, USD(400)), ter(tesSUCCESS));
1763 env.close();
1764
1765 BEAST_EXPECT(amm.expectBalances(
1766 STAmount(USD, UINT64_C(5656854249492380), -13),
1767 XRP(70.710678),
1768 IOUAmount(200000)));
1769 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount(0)));
1770 BEAST_EXPECT(expectLedgerEntryRoot(
1771 env, alice, aliceXrpBalance + XRP(29.289322)));
1772 }
1773
1774 void
1775 run() override
1776 {
1779 testFeatureDisabled(all - featureAMMClawback);
1789 }
1790};
1791BEAST_DEFINE_TESTSUITE(AMMClawback, app, ripple);
1792} // namespace test
1793} // namespace ripple
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:47
A currency issued by an account.
Definition: Issue.h:36
Currency currency
Definition: Issue.h:38
void testInvalidRequest(FeatureBitset features)
void testAMMClawbackSameCurrency(FeatureBitset features)
void testNotHoldingLptoken(FeatureBitset features)
void testSingleDepositAndClawback(FeatureBitset features)
void testAMMClawbackAll(FeatureBitset features)
void testAMMClawbackSpecificAmount(FeatureBitset features)
void testFeatureDisabled(FeatureBitset features)
void testAMMClawbackExceedBalance(FeatureBitset features)
void run() override
Runs the suite.
void testAssetFrozen(FeatureBitset features)
void testAMMClawbackSameIssuerAssets(FeatureBitset features)
void testAMMClawbackIssuesEachOther(FeatureBitset features)
jtx::Account const alice
Definition: AMMTest.h:67
jtx::Account const gw
Definition: AMMTest.h:65
jtx::Account const bob
Definition: AMMTest.h:68
jtx::Account const carol
Definition: AMMTest.h:66
Convenience class to test AMM functionality.
Definition: AMM.h:122
bool expectBalances(STAmount const &asset1, STAmount const &asset2, IOUAmount const &lpt, std::optional< AccountID > const &account=std::nullopt) const
Verify the AMM balances.
Definition: AMM.cpp:230
IOUAmount deposit(std::optional< Account > const &account, LPToken tokens, std::optional< STAmount > const &asset1InDetails=std::nullopt, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< ter > const &ter=std::nullopt)
Definition: AMM.cpp:409
bool expectLPTokens(AccountID const &account, IOUAmount const &tokens) const
Definition: AMM.cpp:260
Immutable cryptographic account descriptor.
Definition: Account.h:39
AccountID id() const
Returns the Account ID.
Definition: Account.h:107
A transaction testing environment.
Definition: Env.h:118
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:530
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:115
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:262
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:231
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition: Env.cpp:177
A balance matches.
Definition: balance.h:39
Match set account flags.
Definition: flags.h:112
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
Set the flags on a JTx.
Definition: txflags.h:31
Json::Value ammClawback(Account const &issuer, Account const &holder, Issue const &asset, Issue const &asset2, std::optional< STAmount > const &amount)
Definition: AMM.cpp:827
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:31
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:28
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
bool expectLedgerEntryRoot(Env &env, Account const &acct, STAmount const &expectedValue)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:104
FeatureBitset supported_amendments()
Definition: Env.h:71
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t asfGlobalFreeze
Definition: TxFlags.h:82
constexpr std::uint32_t tfClawTwoAssets
Definition: TxFlags.h:221
@ tecNO_PERMISSION
Definition: TER.h:292
@ tecAMM_BALANCE
Definition: TER.h:316
@ tesSUCCESS
Definition: TER.h:242
constexpr std::uint32_t tfTwoAssetIfEmpty
Definition: TxFlags.h:210
constexpr std::uint32_t asfAllowTrustLineClawback
Definition: TxFlags.h:93
@ terNO_ACCOUNT
Definition: TER.h:217
@ terNO_AMM
Definition: TER.h:227
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:115
@ temBAD_AMOUNT
Definition: TER.h:89
@ temMALFORMED
Definition: TER.h:87
@ temINVALID_FLAG
Definition: TER.h:111
@ temDISABLED
Definition: TER.h:114