xrpld
Loading...
Searching...
No Matches
LPTokenTransfer_test.cpp
1#include <test/jtx/AMM.h>
2#include <test/jtx/AMMTest.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/check.h>
7#include <test/jtx/mpt.h>
8#include <test/jtx/offer.h>
9#include <test/jtx/owners.h> // IWYU pragma: keep
10#include <test/jtx/pay.h>
11#include <test/jtx/sendmax.h>
12#include <test/jtx/ter.h>
13#include <test/jtx/token.h>
14#include <test/jtx/trust.h>
15#include <test/jtx/txflags.h>
16
17#include <xrpl/basics/base_uint.h>
18#include <xrpl/beast/unit_test/suite.h>
19#include <xrpl/protocol/Feature.h>
20#include <xrpl/protocol/Indexes.h>
21#include <xrpl/protocol/SeqProxy.h>
22#include <xrpl/protocol/TER.h>
23#include <xrpl/protocol/TxFlags.h>
24
25#include <cstdint>
26
27namespace xrpl::test {
28
30{
31 void
33 {
34 testcase("DirectStep");
35
36 using namespace jtx;
37 Env env{*this, features};
38 fund(env, gw_, {alice_}, {USD(20'000), BTC(0.5)}, Fund::All);
39 env.close();
40
41 AMM ammAlice(env, alice_, USD(20'000), BTC(0.5));
42 BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0}));
43
44 fund(env, gw_, {carol_}, {USD(4'000), BTC(1)}, Fund::Acct);
45 ammAlice.deposit(carol_, 10);
46 BEAST_EXPECT(ammAlice.expectBalances(USD(22'000), BTC(0.55), IOUAmount{110, 0}));
47
48 fund(env, gw_, {bob_}, {USD(4'000), BTC(1)}, Fund::Acct);
49 ammAlice.deposit(bob_, 10);
50 BEAST_EXPECT(ammAlice.expectBalances(USD(24'000), BTC(0.60), IOUAmount{120, 0}));
51
52 auto const lpIssue = ammAlice.lptIssue();
53 env.trust(STAmount{lpIssue, 500}, alice_);
54 env.trust(STAmount{lpIssue, 500}, bob_);
55 env.trust(STAmount{lpIssue, 500}, carol_);
56 env.close();
57
58 // gateway freezes carol_'s USD
59 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
60 env.close();
61
62 // bob_ can still send lptoken to carol_ even tho carol_'s USD is
63 // frozen, regardless of whether fixFrozenLPTokenTransfer is enabled or
64 // not
65 // Note: Deep freeze is not considered for LPToken transfer
66 env(pay(bob_, carol_, STAmount{lpIssue, 5}));
67 env.close();
68
69 // cannot transfer to an amm account
70 env(pay(carol_, lpIssue.getIssuer(), STAmount{lpIssue, 5}), Ter(tecNO_PERMISSION));
71 env.close();
72
73 if (features[fixFrozenLPTokenTransfer])
74 {
75 // carol_ is frozen on USD and therefore can't send lptoken to bob_
76 env(pay(carol_, bob_, STAmount{lpIssue, 5}), Ter(tecPATH_DRY));
77 }
78 else
79 {
80 // carol_ can still send lptoken with frozen USD
81 env(pay(carol_, bob_, STAmount{lpIssue, 5}));
82 }
83 }
84
85 void
87 {
88 testcase("BookStep");
89
90 using namespace jtx;
91 Env env{*this, features};
92
93 fund(env, gw_, {alice_, bob_, carol_}, {USD(10'000), EUR(10'000)}, Fund::All);
94 AMM ammAlice(env, alice_, USD(10'000), EUR(10'000));
95 ammAlice.deposit(carol_, 1'000);
96 ammAlice.deposit(bob_, 1'000);
97
98 auto const lpIssue = ammAlice.lptIssue();
99
100 // carols creates an offer to sell lptoken
101 env(offer(carol_, XRP(10), STAmount{lpIssue, 10}), Txflags(tfPassive));
102 env.close();
103 BEAST_EXPECT(expectOffers(env, carol_, 1));
104
105 env.trust(STAmount{lpIssue, 1'000'000'000}, alice_);
106 env.trust(STAmount{lpIssue, 1'000'000'000}, bob_);
107 env.trust(STAmount{lpIssue, 1'000'000'000}, carol_);
108 env.close();
109
110 // gateway freezes carol_'s USD
111 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
112 env.close();
113
114 // exercises alice_'s ability to consume carol_'s offer to sell lptoken
115 // when carol_'s USD is frozen pre/post fixFrozenLPTokenTransfer
116 // amendment
117 if (features[fixFrozenLPTokenTransfer])
118 {
119 // with fixFrozenLPTokenTransfer, alice_ fails to consume carol_'s
120 // offer since carol_'s USD is frozen
121 env(pay(alice_, bob_, STAmount{lpIssue, 10}),
122 Txflags(tfPartialPayment),
123 Sendmax(XRP(10)),
125 env.close();
126 BEAST_EXPECT(expectOffers(env, carol_, 1));
127
128 // gateway unfreezes carol_'s USD
129 env(trust(gw_, carol_["USD"](1'000'000'000), tfClearFreeze));
130 env.close();
131
132 // alice_ successfully consumes carol_'s offer
133 env(pay(alice_, bob_, STAmount{lpIssue, 10}),
134 Txflags(tfPartialPayment),
135 Sendmax(XRP(10)));
136 env.close();
137 BEAST_EXPECT(expectOffers(env, carol_, 0));
138 }
139 else
140 {
141 // without fixFrozenLPTokenTransfer, alice_ can consume carol_'s offer
142 // even when carol_'s USD is frozen
143 env(pay(alice_, bob_, STAmount{lpIssue, 10}),
144 Txflags(tfPartialPayment),
145 Sendmax(XRP(10)));
146 env.close();
147 BEAST_EXPECT(expectOffers(env, carol_, 0));
148 }
149
150 // make sure carol_'s USD is not frozen
151 env(trust(gw_, carol_["USD"](1'000'000'000), tfClearFreeze));
152 env.close();
153
154 // ensure that carol_'s offer to buy lptoken can be consumed by alice_
155 // even when carol_'s USD is frozen
156 {
157 // carol_ creates an offer to buy lptoken
158 env(offer(carol_, STAmount{lpIssue, 10}, XRP(10)), Txflags(tfPassive));
159 env.close();
160 BEAST_EXPECT(expectOffers(env, carol_, 1));
161
162 // gateway freezes carol_'s USD
163 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
164 env.close();
165
166 // alice_ successfully consumes carol_'s offer
167 env(pay(alice_, bob_, XRP(10)),
168 Txflags(tfPartialPayment),
169 Sendmax(STAmount{lpIssue, 10}));
170 env.close();
171 BEAST_EXPECT(expectOffers(env, carol_, 0));
172 }
173 }
174
175 void
177 {
178 testcase("Create offer");
179
180 using namespace jtx;
181 Env env{*this, features};
182
183 fund(env, gw_, {alice_, bob_, carol_}, {USD(10'000), EUR(10'000)}, Fund::All);
184 AMM ammAlice(env, alice_, USD(10'000), EUR(10'000));
185 ammAlice.deposit(carol_, 1'000);
186 ammAlice.deposit(bob_, 1'000);
187
188 auto const lpIssue = ammAlice.lptIssue();
189
190 // gateway freezes carol_'s USD
191 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
192 env.close();
193
194 // exercises carol_'s ability to create a new offer to sell lptoken with
195 // frozen USD, before and after fixFrozenLPTokenTransfer
196 if (features[fixFrozenLPTokenTransfer])
197 {
198 // with fixFrozenLPTokenTransfer, carol_ can't create an offer to
199 // sell lptoken when one of the assets is frozen
200
201 // carol_ can't create an offer to sell lptoken
202 env(offer(carol_, XRP(10), STAmount{lpIssue, 10}),
203 Txflags(tfPassive),
205 env.close();
206 BEAST_EXPECT(expectOffers(env, carol_, 0));
207
208 // gateway unfreezes carol_'s USD
209 env(trust(gw_, carol_["USD"](1'000'000'000), tfClearFreeze));
210 env.close();
211
212 // carol_ can create an offer to sell lptoken after USD is unfrozen
213 env(offer(carol_, XRP(10), STAmount{lpIssue, 10}), Txflags(tfPassive));
214 env.close();
215 BEAST_EXPECT(expectOffers(env, carol_, 1));
216 }
217 else
218 {
219 // without fixFrozenLPTokenTransfer, carol_ can create an offer
220 env(offer(carol_, XRP(10), STAmount{lpIssue, 10}), Txflags(tfPassive));
221 env.close();
222 BEAST_EXPECT(expectOffers(env, carol_, 1));
223 }
224
225 // gateway freezes carol_'s USD
226 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
227 env.close();
228
229 // carol_ can create offer to buy lptoken even if USD is frozen
230 env(offer(carol_, STAmount{lpIssue, 10}, XRP(5)), Txflags(tfPassive));
231 env.close();
232 BEAST_EXPECT(expectOffers(env, carol_, 2));
233 }
234
235 void
237 {
238 testcase("Offer crossing");
239
240 using namespace jtx;
241 Env env{*this, features};
242
243 // Offer crossing with two AMM LPTokens.
244 fund(env, gw_, {alice_, carol_}, {USD(10'000)}, Fund::All);
245 AMM ammAlice1(env, alice_, XRP(10'000), USD(10'000));
246 ammAlice1.deposit(carol_, 10'000'000);
247
248 fund(env, gw_, {alice_, carol_}, {EUR(10'000)}, Fund::TokenOnly);
249 AMM ammAlice2(env, alice_, XRP(10'000), EUR(10'000));
250 ammAlice2.deposit(carol_, 10'000'000);
251 auto const token1 = ammAlice1.lptIssue();
252 auto const token2 = ammAlice2.lptIssue();
253
254 // carol_ creates offer
255 env(offer(carol_, STAmount{token2, 100}, STAmount{token1, 100}));
256 env.close();
257 BEAST_EXPECT(expectOffers(env, carol_, 1));
258
259 // gateway freezes carol_'s USD, carol_'s token1 should be frozen as well
260 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
261 env.close();
262
263 // alice_ creates an offer which exhibits different behavior on offer
264 // crossing depending on if fixFrozenLPTokenTransfer is enabled
265 env(offer(alice_, STAmount{token1, 100}, STAmount{token2, 100}));
266 env.close();
267
268 // exercises carol_'s offer's ability to cross with alice_'s offer when
269 // carol_'s USD is frozen, before and after fixFrozenLPTokenTransfer
270 if (features[fixFrozenLPTokenTransfer])
271 {
272 // with fixFrozenLPTokenTransfer enabled, alice_'s offer can no
273 // longer cross with carol_'s offer
274 BEAST_EXPECT(
275 expectHolding(env, alice_, STAmount{token1, 10'000'000}) &&
276 expectHolding(env, alice_, STAmount{token2, 10'000'000}));
277 BEAST_EXPECT(
278 expectHolding(env, carol_, STAmount{token2, 10'000'000}) &&
279 expectHolding(env, carol_, STAmount{token1, 10'000'000}));
280 BEAST_EXPECT(expectOffers(env, alice_, 1) && expectOffers(env, carol_, 0));
281 }
282 else
283 {
284 // alice_'s offer still crosses with carol_'s offer despite carol_'s
285 // token1 is frozen
286 BEAST_EXPECT(
287 expectHolding(env, alice_, STAmount{token1, 10'000'100}) &&
288 expectHolding(env, alice_, STAmount{token2, 9'999'900}));
289 BEAST_EXPECT(
290 expectHolding(env, carol_, STAmount{token2, 10'000'100}) &&
291 expectHolding(env, carol_, STAmount{token1, 9'999'900}));
292 BEAST_EXPECT(expectOffers(env, alice_, 0) && expectOffers(env, carol_, 0));
293 }
294 }
295
296 void
298 {
299 testcase("Check");
300
301 using namespace jtx;
302 Env env{*this, features};
303
304 fund(env, gw_, {alice_, bob_, carol_}, {USD(10'000), EUR(10'000)}, Fund::All);
305 AMM ammAlice(env, alice_, USD(10'000), EUR(10'000));
306 ammAlice.deposit(carol_, 1'000);
307 ammAlice.deposit(bob_, 1'000);
308
309 auto const lpIssue = ammAlice.lptIssue();
310
311 // gateway freezes carol_'s USD
312 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
313 env.close();
314
315 // carol_ can always create a check with lptoken that has frozen
316 // token
318 env(check::create(carol_, bob_, STAmount{lpIssue, 10}));
319 env.close();
320
321 // with fixFrozenLPTokenTransfer enabled, bob_ fails to cash the check
322 if (features[fixFrozenLPTokenTransfer])
323 {
324 env(check::cash(bob_, carolChkId, STAmount{lpIssue, 10}), Ter(tecPATH_PARTIAL));
325 }
326 else
327 {
328 env(check::cash(bob_, carolChkId, STAmount{lpIssue, 10}));
329 }
330
331 env.close();
332
333 // bob_ creates a check
335 env(check::create(bob_, carol_, STAmount{lpIssue, 10}));
336 env.close();
337
338 // carol_ cashes the bob_'s check. Even though carol_ is frozen, she can
339 // still receive LPToken
340 env(check::cash(carol_, bobChkId, STAmount{lpIssue, 10}));
341 env.close();
342 }
343
344 void
346 {
347 testcase("NFT Offers");
348 using namespace test::jtx;
349
350 Env env{*this, features};
351
352 // Setup AMM
353 fund(env, gw_, {alice_, bob_, carol_}, {USD(10'000), EUR(10'000)}, Fund::All);
354 AMM ammAlice(env, alice_, USD(10'000), EUR(10'000));
355 ammAlice.deposit(carol_, 1'000);
356 ammAlice.deposit(bob_, 1'000);
357
358 auto const lpIssue = ammAlice.lptIssue();
359
360 // bob_ mints a nft
361 UInt256 const nftID{token::getNextID(env, bob_, 0u, tfTransferable)};
362 env(token::mint(bob_, 0), Txflags(tfTransferable));
363 env.close();
364
365 // bob_ creates a sell offer for lptoken
366 UInt256 const sellOfferIndex =
368 env(token::createOffer(bob_, nftID, STAmount{lpIssue, 10}), Txflags(tfSellNFToken));
369 env.close();
370
371 // gateway freezes carol_'s USD
372 env(trust(gw_, carol_["USD"](0), tfSetFreeze));
373 env.close();
374
375 // exercises one's ability to transfer NFT using lptoken when one of the
376 // assets is frozen
377 if (features[fixFrozenLPTokenTransfer])
378 {
379 // with fixFrozenLPTokenTransfer, freezing USD will prevent buy/sell
380 // offers with lptokens from being created/accepted
381
382 // carol_ fails to accept bob_'s offer with lptoken because carol_'s
383 // USD is frozen
385 env.close();
386
387 // gateway unfreezes carol_'s USD
388 env(trust(gw_, carol_["USD"](1'000'000), tfClearFreeze));
389 env.close();
390
391 // carol_ can now accept the offer and own the nft
392 env(token::acceptSellOffer(carol_, sellOfferIndex));
393 env.close();
394
395 // gateway freezes bob_'s USD
396 env(trust(gw_, bob_["USD"](0), tfSetFreeze));
397 env.close();
398
399 // bob_ fails to create a buy offer with lptoken for carol_'s nft
400 // since bob_'s USD is frozen
401 env(token::createOffer(bob_, nftID, STAmount{lpIssue, 10}),
404 env.close();
405
406 // gateway unfreezes bob_'s USD
407 env(trust(gw_, bob_["USD"](1'000'000), tfClearFreeze));
408 env.close();
409
410 // bob_ can now create a buy offer
411 env(token::createOffer(bob_, nftID, STAmount{lpIssue, 10}), token::Owner(carol_));
412 env.close();
413 }
414 else
415 {
416 // without fixFrozenLPTokenTransfer, freezing USD will still allow
417 // buy/sell offers to be created/accepted with lptoken
418
419 // carol_ can still accept bob_'s offer despite carol_'s USD is frozen
420 env(token::acceptSellOffer(carol_, sellOfferIndex));
421 env.close();
422
423 // gateway freezes bob_'s USD
424 env(trust(gw_, bob_["USD"](0), tfSetFreeze));
425 env.close();
426
427 // bob_ creates a buy offer with lptoken despite bob_'s USD is frozen
428 UInt256 const buyOfferIndex =
430 env(token::createOffer(bob_, nftID, STAmount{lpIssue, 10}), token::Owner(carol_));
431 env.close();
432
433 // carol_ accepts bob_'s offer
434 env(token::acceptBuyOffer(carol_, buyOfferIndex));
435 env.close();
436 }
437 }
438
439 void
441 {
442 testcase("MPT CanTransfer DirectStep");
443
444 using namespace jtx;
445
446 // An MPT can only be an AMM pool asset once featureMPTokensV2 is
447 // enabled, so this behavior is only meaningful when V2 is present, and
448 // is independent of fixFrozenLPTokenTransfer.
449 if (!features[featureMPTokensV2])
450 return;
451
452 // gw issues an MPT used as one of the AMM pool assets. gw (the MPT
453 // issuer) seeds the pool and hands LP tokens to alice. Transferring LP
454 // tokens between two non-issuer holders is only permitted when the
455 // pool MPT allows transfers (lsfMPTCanTransfer); issuer-involving
456 // transfers are always permitted. The check fires on the redeem step
457 // against the AMM account via canTransferLPToken().
458 auto testLPTokenTransfer = [&](std::uint32_t mptFlags, bool poolXrpToBtc) {
459 Env env{*this, features};
460 env.fund(XRP(30'000), gw_, alice_, bob_);
461 env.close();
462
463 // gw is the MPT issuer, so it may seed the pool regardless of
464 // whether the MPT permits third-party transfers.
465 MPT const btc = MPTTester(
466 {.env = env, .issuer = gw_, .holders = {alice_}, .pay = 1'000, .flags = mptFlags});
467
468 auto const asset1 = poolXrpToBtc ? XRP(10'000) : btc(10'000);
469 auto const asset2 = poolXrpToBtc ? btc(10'000) : XRP(10'000);
470 AMM const amm(env, gw_, asset1, asset2);
471 auto const lpIssue = amm.lptIssue();
472
473 env.trust(STAmount{lpIssue, 100'000}, alice_);
474 env.trust(STAmount{lpIssue, 100'000}, bob_);
475 env.close();
476
477 // Issuer-involving LP token transfer is always allowed (gw is the
478 // pool MPT's issuer), even when the MPT lacks CanTransfer.
479 env(pay(gw_, alice_, STAmount{lpIssue, 1'000}));
480 env.close();
481
482 // Transfer between two non-issuer holders is allowed only if the
483 // pool MPT has CanTransfer set; otherwise the redeem step against
484 // the AMM account blocks it with tecNO_AUTH.
485 if ((mptFlags & tfMPTCanTransfer) != 0u)
486 {
487 env(pay(alice_, bob_, STAmount{lpIssue, 100}));
488 }
489 else
490 {
491 env(pay(alice_, bob_, STAmount{lpIssue, 100}), Ter(tecNO_AUTH));
492 }
493 env.close();
494 };
495
496 // Pool MPT without CanTransfer blocks third-party LP token transfers.
497 testLPTokenTransfer(tfMPTCanTrade, true);
498 testLPTokenTransfer(tfMPTCanTrade, false);
499
500 // Pool MPT with CanTransfer allows them.
501 testLPTokenTransfer(tfMPTCanTrade | tfMPTCanTransfer, true);
502 testLPTokenTransfer(tfMPTCanTrade | tfMPTCanTransfer, false);
503 }
504
505 void
507 {
508 testcase("MPT CanTransfer Offer");
509
510 using namespace jtx;
511
512 if (!features[featureMPTokensV2])
513 return;
514
515 // Parity with frozen LP tokens for the order book: a non-transferable
516 // pool MPT makes the LP token un-spendable (canTransferLPToken zeroes
517 // the spendable balance in accountHolds, just as isLPTokenFrozen does),
518 // so an offer to sell it cannot be funded - the same tecUNFUNDED_OFFER
519 // outcome as freezing a pool asset (see testOfferCreation).
520 auto testLPTokenTransfer = [&](std::uint32_t mptFlags, bool poolXrpToBtc) {
521 Env env{*this, features};
522 env.fund(XRP(30'000), gw_, carol_);
523 env.close();
524
525 MPT const btc = MPTTester(
526 {.env = env, .issuer = gw_, .holders = {carol_}, .pay = 1'000, .flags = mptFlags});
527
528 auto const asset1 = poolXrpToBtc ? XRP(10'000) : btc(10'000);
529 auto const asset2 = poolXrpToBtc ? btc(10'000) : XRP(10'000);
530 AMM const amm(env, gw_, asset1, asset2);
531 auto const lpIssue = amm.lptIssue();
532
533 env.trust(STAmount{lpIssue, 100'000}, carol_);
534 env.close();
535
536 // gw (the pool MPT issuer) seeds carol_ with LP tokens; issuer
537 // involving transfers are always allowed.
538 env(pay(gw_, carol_, STAmount{lpIssue, 1'000}));
539 env.close();
540
541 // carol_ tries to create an offer to sell the LP token.
542 if ((mptFlags & tfMPTCanTransfer) != 0u)
543 {
544 env(offer(carol_, XRP(10), STAmount{lpIssue, 10}), Txflags(tfPassive));
545 env.close();
546 BEAST_EXPECT(expectOffers(env, carol_, 1));
547 }
548 else
549 {
550 // Non-transferable pool MPT => LP token un-spendable => the
551 // sell offer is unfunded, just as if a pool asset were frozen.
552 env(offer(carol_, XRP(10), STAmount{lpIssue, 10}),
553 Txflags(tfPassive),
555 env.close();
556 BEAST_EXPECT(expectOffers(env, carol_, 0));
557 }
558 };
559
560 // Pool MPT without CanTransfer: LP token sell offer is unfunded.
561 testLPTokenTransfer(tfMPTCanTrade, true);
562 testLPTokenTransfer(tfMPTCanTrade, false);
563
564 // Pool MPT with CanTransfer: LP token sell offer is created.
565 testLPTokenTransfer(tfMPTCanTrade | tfMPTCanTransfer, true);
566 testLPTokenTransfer(tfMPTCanTrade | tfMPTCanTransfer, false);
567 }
568
569public:
570 void
571 run() override
572 {
574
575 for (auto const features : {all, all - fixFrozenLPTokenTransfer})
576 {
577 testDirectStep(features);
578 testBookStep(features);
579 testOfferCreation(features);
580 testOfferCrossing(features);
581 testCheck(features);
582 testNFTOffers(features);
584 testMPTCanTransferOffer(features);
585 }
586 }
587};
588
589BEAST_DEFINE_TESTSUITE(LPTokenTransfer, app, xrpl);
590} // namespace xrpl::test
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:26
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:62
void testMPTCanTransferDirectStep(FeatureBitset features)
void run() override
Runs the suite.
void testOfferCrossing(FeatureBitset features)
void testOfferCreation(FeatureBitset features)
void testMPTCanTransferOffer(FeatureBitset features)
void testDirectStep(FeatureBitset features)
void testBookStep(FeatureBitset features)
void testCheck(FeatureBitset features)
void testNFTOffers(FeatureBitset features)
jtx::Account const alice_
Definition AMMTest.h:86
jtx::Account const gw_
Definition AMMTest.h:84
jtx::Account const carol_
Definition AMMTest.h:85
jtx::Account const bob_
Definition AMMTest.h:87
Convenience class to test AMM functionality.
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:463
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:269
A transaction testing environment.
Definition Env.h:161
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:302
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:354
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
Definition mpt.h:512
Converts to MPT Issue or STAmount.
Sets the SendMax on a JTx.
Definition sendmax.h:16
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
Set the flags on a JTx.
Definition txflags.h:14
Sets the optional Owner on an NFTokenOffer.
Definition token.h:132
Keylet nftokenOffer(AccountID const &owner, SeqProxy const &seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:453
Keylet check(AccountID const &id, SeqProxy const &seq) noexcept
A Check.
Definition Indexes.cpp:360
json::Value cash(jtx::Account const &dest, UInt256 const &checkId, STAmount const &amount)
Cash a check requiring that a specific amount be delivered.
Definition check.cpp:15
json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
json::Value acceptSellOffer(jtx::Account const &account, UInt256 const &offerIndex)
Accept an NFToken sell offer.
Definition token.cpp:169
json::Value mint(jtx::Account const &account, std::uint32_t nfTokenTaxon)
Mint an NFToken.
Definition token.cpp:23
json::Value createOffer(jtx::Account const &account, UInt256 const &nftokenID, STAmount const &amount)
Create an NFTokenOffer.
Definition token.cpp:96
UInt256 getNextID(jtx::Env const &env, jtx::Account const &issuer, std::uint32_t nfTokenTaxon, std::uint16_t flags, std::uint16_t xferFee)
Get the next NFTokenID that will be issued.
Definition token.cpp:57
json::Value acceptBuyOffer(jtx::Account const &account, UInt256 const &offerIndex)
Accept an NFToken buy offer.
Definition token.cpp:159
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
std::vector< STAmount > fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:34
bool expectOffers(Env &env, AccountID const &account, std::uint16_t size, std::vector< Amounts > const &toMatch)
bool expectHolding(Env &env, AccountID const &account, STAmount const &value, bool defaultLimits)
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
FeatureBitset testableAmendments()
Definition Env.h:92
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:18
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
BaseUInt< 256 > UInt256
Definition base_uint.h:580
@ tecPATH_PARTIAL
Definition TER.h:290
@ tecPATH_DRY
Definition TER.h:302
@ tecNO_AUTH
Definition TER.h:308
@ tecINSUFFICIENT_FUNDS
Definition TER.h:333
@ tecUNFUNDED_OFFER
Definition TER.h:292
@ tecNO_PERMISSION
Definition TER.h:313
UInt256 key
Definition Keylet.h:21