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