xrpld
Loading...
Searching...
No Matches
AMM.cpp
1#include <test/jtx/AMM.h>
2
3#include <test/jtx/Account.h>
4#include <test/jtx/Env.h>
5#include <test/jtx/multisign.h>
6#include <test/jtx/seq.h>
7#include <test/jtx/ter.h>
8
9#include <xrpl/basics/Number.h>
10#include <xrpl/basics/contract.h>
11#include <xrpl/basics/safe_cast.h>
12#include <xrpl/json/json_value.h>
13#include <xrpl/json/to_string.h>
14#include <xrpl/ledger/helpers/AMMHelpers.h>
15#include <xrpl/ledger/helpers/TokenHelpers.h>
16#include <xrpl/protocol/AMMCore.h>
17#include <xrpl/protocol/AccountID.h>
18#include <xrpl/protocol/AmountConversions.h>
19#include <xrpl/protocol/ApiVersion.h>
20#include <xrpl/protocol/Asset.h>
21#include <xrpl/protocol/Feature.h>
22#include <xrpl/protocol/IOUAmount.h>
23#include <xrpl/protocol/Indexes.h>
24#include <xrpl/protocol/SField.h>
25#include <xrpl/protocol/STAmount.h>
26#include <xrpl/protocol/STArray.h>
27#include <xrpl/protocol/TER.h>
28#include <xrpl/protocol/TxFlags.h>
29#include <xrpl/protocol/jss.h>
30
31#include <algorithm>
32#include <cstdint>
33#include <iostream>
34#include <optional>
35#include <stdexcept>
36#include <string>
37#include <tuple>
38#include <utility>
39#include <vector>
40
41namespace xrpl::test::jtx {
42
43static Number
45{
46 if (isXRP(a))
47 return a.xrp();
48 return a;
49}
50
53{
54 if (!env_.enabled(fixAMMv1_3))
55 {
56 auto const product = number(asset1_) * number(asset2_);
57 return (IOUAmount)(product.mantissa() >= 0 ? root2(product) : root2(-product));
58 }
59 return getLPTokensBalance();
60}
61
63 Env& env,
64 Account account,
65 STAmount asset1,
66 STAmount asset2,
67 bool log,
68 std::uint16_t tfee,
69 std::uint32_t fee,
73 std::optional<Ter> const& ter,
74 bool close)
75 : env_(env)
76 , creatorAccount_(std::move(account))
77 , asset1_(std::move(asset1))
78 , asset2_(std::move(asset2))
79 , ammID_(keylet::amm(asset1_.asset(), asset2_.asset()).key)
80 , log_(log)
81 , doClose_(close)
83 , msig_(std::move(ms))
84 , fee_(fee)
85 , ammAccount_(create(tfee, flags, seq, ter))
88{
89}
90
92 Env& env,
93 Account const& account,
94 STAmount const& asset1,
95 STAmount const& asset2,
96 Ter const& ter,
97 bool log,
98 bool close)
99 : AMM(env,
100 account,
101 asset1,
102 asset2,
103 log,
104 0,
105 0,
106 std::nullopt,
107 std::nullopt,
108 std::nullopt,
109 ter,
110 close)
111{
112}
113
115 Env& env,
116 Account const& account,
117 STAmount const& asset1,
118 STAmount const& asset2,
119 CreateArg const& arg)
120 : AMM(env,
121 account,
122 asset1,
123 asset2,
124 arg.log,
125 arg.tfee,
126 arg.fee,
127 arg.flags,
128 arg.seq,
129 arg.ms,
130 arg.err,
131 arg.close)
132{
133}
134
137 AccountID const& account,
138 STAmount const& asset1,
139 STAmount const& asset2,
140 std::uint16_t const& tfee)
141{
142 json::Value jv;
143 jv[jss::Account] = to_string(account);
144 jv[jss::Amount] = asset1.getJson(JsonOptions::Values::None);
145 jv[jss::Amount2] = asset2.getJson(JsonOptions::Values::None);
146 jv[jss::TradingFee] = tfee;
147 jv[jss::TransactionType] = jss::AMMCreate;
148
149 return jv;
150}
151
152[[nodiscard]] AccountID
154 std::uint32_t tfee,
155 std::optional<std::uint32_t> const& flags,
156 std::optional<jtx::Seq> const& seq,
157 std::optional<Ter> const& ter)
158{
160 if (flags)
161 jv[jss::Flags] = *flags;
162 if (fee_ != 0)
163 {
164 jv[sfFee] = std::to_string(fee_);
165 }
166 else
167 {
168 jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops());
169 }
170 submit(jv, seq, ter);
171
172 if (!ter || env_.ter() == tesSUCCESS)
173 {
174 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
175 {
176 return amm->getAccountID(sfAccount);
177 }
178 }
179 return {};
180}
181
184 std::optional<AccountID> const& account,
185 std::optional<std::string> const& ledgerIndex,
186 std::optional<Asset> const& asset1,
187 std::optional<Asset> const& asset2,
189 bool ignoreParams,
190 unsigned apiVersion) const
191{
192 auto const toJson = [](AccountID const& a) { return json::Value{to_string(a)}; };
193
194 return ammRpcInfo(
195 account.transform(toJson),
196 ledgerIndex,
197 asset1,
198 asset2,
199 ammAccount.transform(toJson),
200 ignoreParams,
201 apiVersion);
202}
203
206 std::optional<json::Value> const& account,
207 std::optional<std::string> const& ledgerIndex,
208 std::optional<Asset> const& asset1,
209 std::optional<Asset> const& asset2,
211 bool ignoreParams,
212 unsigned apiVersion) const
213{
214 json::Value jv;
215 if (account)
216 jv[jss::account] = *account;
217 if (ledgerIndex)
218 jv[jss::ledger_index] = *ledgerIndex;
219 if (!ignoreParams)
220 {
221 if (asset1 || asset2)
222 {
223 if (asset1)
224 jv[jss::asset] = STIssue(sfAsset, *asset1).getJson(JsonOptions::Values::None);
225 if (asset2)
226 jv[jss::asset2] = STIssue(sfAsset2, *asset2).getJson(JsonOptions::Values::None);
227 }
228 else if (!ammAccount)
229 {
230 jv[jss::asset] = STIssue(sfAsset, asset1_.asset()).getJson(JsonOptions::Values::None);
231 jv[jss::asset2] = STIssue(sfAsset2, asset2_.asset()).getJson(JsonOptions::Values::None);
232 }
233 if (ammAccount)
234 jv[jss::amm_account] = *ammAccount;
235 }
236 auto jr =
237 (apiVersion == rpc::kApiInvalidVersion
238 ? env_.rpc("json", "amm_info", to_string(jv))
239 : env_.rpc(apiVersion, "json", "amm_info", to_string(jv)));
240 if (jr.isObject() && jr.isMember(jss::result) && jr[jss::result].isMember(jss::status))
241 return jr[jss::result];
243}
244
246AMM::balances(Asset const& asset1, Asset const& asset2, std::optional<AccountID> const& account)
247 const
248{
249 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
250 {
251 auto const ammAccountID = amm->getAccountID(sfAccount);
252 auto const [asset1Balance, asset2Balance] = ammPoolHolds(
253 *env_.current(),
254 ammAccountID,
255 asset1,
256 asset2,
259 env_.journal);
260 auto const lptAMMBalance = account
261 ? ammLPHolds(*env_.current(), *amm, *account, env_.journal)
262 : amm->getFieldAmount(sfLPTokenBalance);
263 return {asset1Balance, asset2Balance, lptAMMBalance};
264 }
265 return {STAmount{}, STAmount{}, STAmount{}};
266}
267
268bool
270 STAmount const& asset1,
271 STAmount const& asset2,
272 IOUAmount const& lpt,
273 std::optional<AccountID> const& account) const
274{
275 auto const [asset1Balance, asset2Balance, lptAMMBalance] =
276 balances(asset1.asset(), asset2.asset(), account);
277 return asset1 == asset1Balance && asset2 == asset2Balance &&
278 lptAMMBalance == STAmount{lpt, lptIssue_};
279}
280
283{
284 if (account)
285 {
286 return accountHolds(
287 *env_.current(), *account, lptIssue_, FreezeHandling::ZeroIfFrozen, env_.journal)
288 .iou();
289 }
290 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
291 return amm->getFieldAmount(sfLPTokenBalance).iou();
292 return IOUAmount{0};
293}
294
295bool
296AMM::expectLPTokens(AccountID const& account, IOUAmount const& expTokens) const
297{
298 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
299 {
300 auto const lptAMMBalance = ammLPHolds(*env_.current(), *amm, account, env_.journal);
301 return lptAMMBalance == STAmount{expTokens, lptIssue_};
302 }
303 return false;
304}
305
306bool
308 std::uint32_t fee,
310 IOUAmount expectedPrice) const
311{
312 return expectAuctionSlot([&](std::uint32_t slotFee,
313 std::optional<std::uint8_t> slotInterval,
314 IOUAmount const& slotPrice,
315 auto const&) {
316 return slotFee == fee &&
317 // Auction slot might be expired, in which case slotInterval is
318 // 0
319 ((!timeSlot && slotInterval == 0) || slotInterval == timeSlot) &&
320 slotPrice == expectedPrice;
321 });
322}
323
324bool
326{
327 return expectAuctionSlot(
328 [&](std::uint32_t, std::optional<std::uint8_t>, IOUAmount const&, STArray const& accounts) {
329 return std::ranges::all_of(accounts, [&](auto const& account) {
330 return std::ranges::find(authAccounts, account.getAccountID(sfAccount)) !=
331 authAccounts.end();
332 });
333 });
334}
335
336bool
338{
339 auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset()));
340 return amm && (*amm)[sfTradingFee] == fee;
341}
342
343bool
345{
346 return env_.current()->read(keylet::account(ammAccount_)) != nullptr &&
347 env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())) != nullptr;
348}
349
350bool
352 STAmount const& asset1,
353 STAmount const& asset2,
354 IOUAmount const& balance,
355 std::optional<AccountID> const& account,
356 std::optional<std::string> const& ledgerIndex,
358{
359 auto const jv = ammRpcInfo(account, ledgerIndex, std::nullopt, std::nullopt, ammAccount);
360 return expectAmmInfo(asset1, asset2, balance, jv);
361}
362
363bool
365 STAmount const& asset1,
366 STAmount const& asset2,
367 IOUAmount const& balance,
368 json::Value const& jvRes) const
369{
370 if (!jvRes.isMember(jss::amm))
371 return false;
372 auto const& jv = jvRes[jss::amm];
373 if (!jv.isMember(jss::amount) || !jv.isMember(jss::amount2) || !jv.isMember(jss::lp_token))
374 return false;
375 STAmount asset1Info;
376 if (!amountFromJsonNoThrow(asset1Info, jv[jss::amount]))
377 return false;
378 STAmount asset2Info;
379 if (!amountFromJsonNoThrow(asset2Info, jv[jss::amount2]))
380 return false;
381 STAmount lptBalance;
382 if (!amountFromJsonNoThrow(lptBalance, jv[jss::lp_token]))
383 return false;
384 // ammRpcInfo returns unordered assets
385 if (asset1Info.asset() != asset1.asset())
386 std::swap(asset1Info, asset2Info);
387 return asset1 == asset1Info && asset2 == asset2Info &&
388 lptBalance == STAmount{balance, lptIssue_};
389}
390
391void
393{
394 if (assets)
395 {
396 jv[jss::Asset] = STIssue(sfAsset, assets->first).getJson(JsonOptions::Values::None);
397 jv[jss::Asset2] = STIssue(sfAsset, assets->second).getJson(JsonOptions::Values::None);
398 }
399 else
400 {
401 jv[jss::Asset] = STIssue(sfAsset, asset1_.asset()).getJson(JsonOptions::Values::None);
402 jv[jss::Asset2] = STIssue(sfAsset, asset2_.asset()).getJson(JsonOptions::Values::None);
403 }
404}
405
408{
409 json::Value jv;
410 if (!arg.account || !arg.assets)
411 Throw<std::runtime_error>("AMM::depositJv: account or assets not set");
412
413 jv[jss::Account] = arg.account->human();
414 jv[jss::Asset] = STIssue(sfAsset, arg.assets->first).getJson(JsonOptions::Values::None);
415 jv[jss::Asset2] = STIssue(sfAsset, arg.assets->second).getJson(JsonOptions::Values::None);
416 if (arg.tokens)
417 arg.tokens->tokens().setJson(jv[jss::LPTokenOut]);
418 if (arg.asset1In)
419 arg.asset1In->setJson(jv[jss::Amount]);
420 if (arg.asset2In)
421 arg.asset2In->setJson(jv[jss::Amount2]);
422 if (arg.maxEP)
423 arg.maxEP->setJson(jv[jss::EPrice]);
424 if (arg.tfee)
425 jv[jss::TradingFee] = *arg.tfee;
426 std::uint32_t flags = 0;
427 if (arg.flags)
428 flags = *arg.flags;
429 // If including asset1In and asset2In or tokens as
430 // deposit min amounts then must set the flags
431 // explicitly instead of relying on this logic.
432 if ((flags & tfDepositSubTx) == 0u)
433 {
434 if (arg.tokens && !arg.asset1In)
435 {
436 flags |= tfLPToken;
437 }
438 else if (arg.tokens && arg.asset1In)
439 {
440 flags |= tfOneAssetLPToken;
441 }
442 else if (arg.asset1In && arg.asset2In)
443 {
444 flags |= tfTwoAsset;
445 }
446 else if (arg.maxEP && arg.asset1In)
447 {
448 flags |= tfLimitLPToken;
449 }
450 else if (arg.asset1In)
451 {
452 flags |= tfSingleAsset;
453 }
454 }
455 jv[jss::Flags] = flags;
456
457 jv[jss::TransactionType] = jss::AMMDeposit;
458
459 return jv;
460}
461
464 std::optional<Account> const& account,
466 std::optional<STAmount> const& asset1In,
467 std::optional<std::uint32_t> const& flags,
468 std::optional<Ter> const& ter)
469{
470 return deposit(
471 account,
472 tokens,
473 asset1In,
474 std::nullopt,
475 std::nullopt,
476 flags,
477 std::nullopt,
478 std::nullopt,
479 std::nullopt,
480 ter);
481}
482
485 std::optional<Account> const& account,
486 STAmount const& asset1In,
487 std::optional<STAmount> const& asset2In,
488 std::optional<STAmount> const& maxEP,
489 std::optional<std::uint32_t> const& flags,
490 std::optional<Ter> const& ter)
491{
492 if (asset2In && maxEP)
493 Throw<std::runtime_error>("Invalid options: asset2In and maxEP");
494 return deposit(
495 account,
496 std::nullopt,
497 asset1In,
498 asset2In,
499 maxEP,
500 flags,
501 std::nullopt,
502 std::nullopt,
503 std::nullopt,
504 ter);
505}
506
509 std::optional<Account> const& account,
511 std::optional<STAmount> const& asset1In,
512 std::optional<STAmount> const& asset2In,
513 std::optional<STAmount> const& maxEP,
514 std::optional<std::uint32_t> const& flags,
516 std::optional<jtx::Seq> const& seq,
518 std::optional<Ter> const& ter)
519{
520 auto const acct = account ? account : creatorAccount_;
521 auto const lpTokens = getLPTokensBalance(acct);
523 {.account = acct,
524 .tokens = tokens ? tokens->tokens(lptIssue_) : tokens,
525 .asset1In = asset1In,
526 .asset2In = asset2In,
527 .maxEP = maxEP,
528 .flags = flags,
529 .assets = assets ? assets : std::make_pair(asset1_.asset(), asset2_.asset()),
530 .tfee = tfee});
531 if (fee_ != 0)
532 jv[jss::Fee] = std::to_string(fee_);
533 submit(jv, seq, ter);
534 return getLPTokensBalance(acct) - lpTokens;
535}
536
539{
540 return deposit(
541 arg.account,
542 arg.tokens,
543 arg.asset1In,
544 arg.asset2In,
545 arg.maxEP,
546 arg.flags,
547 arg.assets,
548 arg.seq,
549 arg.tfee,
550 arg.err);
551}
552
555{
556 json::Value jv;
557 if (!arg.account || !arg.assets)
558 Throw<std::runtime_error>("AMM::withdrawJv: account or assets not set");
559 jv[jss::Account] = arg.account->human();
560 jv[jss::Asset] = STIssue(sfAsset, arg.assets->first).getJson(JsonOptions::Values::None);
561 jv[jss::Asset2] = STIssue(sfAsset, arg.assets->second).getJson(JsonOptions::Values::None);
562 if (arg.tokens)
563 arg.tokens->tokens().setJson(jv[jss::LPTokenIn]);
564 if (arg.asset1Out)
565 arg.asset1Out->setJson(jv[jss::Amount]);
566 if (arg.asset2Out)
567 arg.asset2Out->setJson(jv[jss::Amount2]);
568 if (arg.maxEP)
569 arg.maxEP->tokens().setJson(jv[jss::EPrice]);
570 std::uint32_t flags = 0;
571 if (arg.flags)
572 flags = *arg.flags;
573 if ((flags & tfWithdrawSubTx) == 0u)
574 {
575 if (arg.tokens && !arg.asset1Out)
576 {
577 flags |= tfLPToken;
578 }
579 else if (arg.asset1Out && arg.asset2Out)
580 {
581 flags |= tfTwoAsset;
582 }
583 else if (arg.tokens && arg.asset1Out)
584 {
585 flags |= tfOneAssetLPToken;
586 }
587 else if (arg.asset1Out && arg.maxEP)
588 {
589 flags |= tfLimitLPToken;
590 }
591 else if (arg.asset1Out)
592 {
593 flags |= tfSingleAsset;
594 }
595 }
596 jv[jss::Flags] = flags;
597
598 jv[jss::TransactionType] = jss::AMMWithdraw;
599
600 return jv;
601}
602
605 std::optional<Account> const& account,
607 std::optional<STAmount> const& asset1Out,
608 std::optional<std::uint32_t> const& flags,
609 std::optional<Ter> const& ter)
610{
611 return withdraw(
612 account,
613 tokens,
614 asset1Out,
615 std::nullopt,
616 std::nullopt,
617 flags,
618 std::nullopt,
619 std::nullopt,
620 ter);
621}
622
625 std::optional<Account> const& account,
626 STAmount const& asset1Out,
627 std::optional<STAmount> const& asset2Out,
628 std::optional<LPToken> const& maxEP,
629 std::optional<Ter> const& ter)
630{
631 if (asset2Out && maxEP)
632 Throw<std::runtime_error>("Invalid options: asset2Out and maxEP");
633 return withdraw(
634 account,
635 std::nullopt,
636 asset1Out,
637 asset2Out,
638 maxEP,
639 std::nullopt,
640 std::nullopt,
641 std::nullopt,
642 ter);
643}
644
647 std::optional<Account> const& account,
649 std::optional<STAmount> const& asset1Out,
650 std::optional<STAmount> const& asset2Out,
651 std::optional<LPToken> const& maxEP,
652 std::optional<std::uint32_t> const& flags,
654 std::optional<jtx::Seq> const& seq,
655 std::optional<Ter> const& ter)
656{
657 auto const acct = account ? account : creatorAccount_;
658 auto const lpTokens = getLPTokensBalance(acct);
660 .account = acct,
661 .tokens = tokens ? tokens->tokens(lptIssue_) : tokens,
662 .asset1Out = asset1Out,
663 .asset2Out = asset2Out,
664 .maxEP = maxEP ? maxEP->tokens(lptIssue_) : maxEP,
665 .flags = flags,
666 .assets = assets ? assets : std::make_pair(asset1_.asset(), asset2_.asset()),
667 });
668 if (fee_ != 0)
669 jv[jss::Fee] = std::to_string(fee_);
670 submit(jv, seq, ter);
671 return lpTokens - getLPTokensBalance(acct);
672}
673
676{
677 return withdraw(
678 arg.account,
679 arg.tokens,
680 arg.asset1Out,
681 arg.asset2Out,
682 arg.maxEP,
683 arg.flags,
684 arg.assets,
685 arg.seq,
686 arg.err);
687}
688
691{
692 json::Value jv;
693 if (!arg.account || !arg.assets)
694 Throw<std::runtime_error>("AMM::withdrawJv: account or assets not set");
695 jv[jss::Account] = arg.account->human();
696 jv[jss::Asset] = STIssue(sfAsset, arg.assets->first).getJson(JsonOptions::Values::None);
697 jv[jss::Asset2] = STIssue(sfAsset, arg.assets->second).getJson(JsonOptions::Values::None);
698 jv[jss::TradingFee] = arg.tfee;
699 if (arg.flags)
700 jv[jss::Flags] = *arg.flags;
701
702 jv[jss::TransactionType] = jss::AMMVote;
703
704 return jv;
705}
706
707void
709 std::optional<Account> const& account,
710 std::uint32_t feeVal,
711 std::optional<std::uint32_t> const& flags,
712 std::optional<jtx::Seq> const& seq,
714 std::optional<Ter> const& ter)
715{
716 json::Value jv = voteJv({
717 .account = account ? account : creatorAccount_,
718 .tfee = feeVal,
719 .flags = flags,
720 .assets = assets ? assets : std::make_pair(asset1_.asset(), asset2_.asset()),
721 });
722 if (fee_ != 0)
723 jv[jss::Fee] = std::to_string(fee_);
724 submit(jv, seq, ter);
725}
726
727void
729{
730 vote(arg.account, arg.tfee, arg.flags, arg.seq, arg.assets, arg.err);
731}
732
734AMM::bid(BidArg const& arg)
735{
736 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
737 {
738 if (!amm->isFieldPresent(sfAuctionSlot))
739 Throw<std::runtime_error>("AMM::Bid");
740 if (amm->isFieldPresent(sfAuctionSlot))
741 {
742 auto const& auctionSlot =
743 safeDowncast<STObject const&>(amm->peekAtField(sfAuctionSlot));
744 lastPurchasePrice_ = auctionSlot[sfPrice].iou();
745 }
746 }
747 bidMin_ = std::nullopt;
748 bidMax_ = std::nullopt;
749
750 json::Value jv;
751 jv[jss::Account] = arg.account ? arg.account->human() : creatorAccount_.human();
752 setTokens(jv, arg.assets);
753 auto getBid = [&](auto const& bid) {
755 {
756 return STAmount{lptIssue_, std::get<int>(bid)};
757 }
759 {
760 return toSTAmount(std::get<IOUAmount>(bid), lptIssue_);
761 }
762
763 return std::get<STAmount>(bid);
764 };
765 if (arg.bidMin)
766 {
767 STAmount const saTokens = getBid(*arg.bidMin);
768 saTokens.setJson(jv[jss::BidMin]);
769 bidMin_ = saTokens.iou();
770 }
771 if (arg.bidMax)
772 {
773 STAmount const saTokens = getBid(*arg.bidMax);
774 saTokens.setJson(jv[jss::BidMax]);
775 bidMax_ = saTokens.iou();
776 }
777 if (!arg.authAccounts.empty())
778 {
780 for (auto const& account : arg.authAccounts)
781 {
782 json::Value acct;
783 json::Value authAcct;
784 acct[jss::Account] = account.human();
785 authAcct[jss::AuthAccount] = acct;
786 accounts.append(authAcct);
787 }
788 jv[jss::AuthAccounts] = accounts;
789 }
790 if (arg.flags)
791 jv[jss::Flags] = *arg.flags;
792 jv[jss::TransactionType] = jss::AMMBid;
793 if (fee_ != 0)
794 jv[jss::Fee] = std::to_string(fee_);
795 return jv;
796}
797
798void
800{
801 auto const& [asset, asset2] = [&]() {
802 if (arg.assets)
803 return *arg.assets;
804 return std::make_pair(asset1_.asset(), asset2_.asset());
805 }();
806 auto jv = amm::ammClawback(arg.issuer, arg.holder, asset, asset2, arg.amount);
807 if (arg.flags)
808 jv[jss::Flags] = *arg.flags;
809 if (fee_ != 0)
810 jv[jss::Fee] = std::to_string(fee_);
811 submit(jv, std::nullopt, arg.err);
812}
813
814void
816 json::Value const& jv,
817 std::optional<jtx::Seq> const& seq,
818 std::optional<Ter> const& ter)
819{
820 if (log_)
822 if (msig_)
823 {
824 if (seq && ter)
825 {
826 env_(jv, *msig_, *seq, *ter);
827 }
828 else if (seq)
829 {
830 env_(jv, *msig_, *seq);
831 }
832 else if (ter)
833 {
834 env_(jv, *msig_, *ter);
835 }
836 else
837 {
838 env_(jv, *msig_);
839 }
840 }
841 else if (seq && ter)
842 {
843 env_(jv, *seq, *ter);
844 }
845 else if (seq)
846 {
847 env_(jv, *seq);
848 }
849 else if (ter)
850 {
851 env_(jv, *ter);
852 }
853 else
854 {
855 env_(jv);
856 }
857 if (doClose_)
858 env_.close();
859}
860
861bool
863{
864 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
865 {
866 if (!amm->isFieldPresent(sfAuctionSlot))
867 Throw<std::runtime_error>("AMM::expectAuctionSlot");
868 if (amm->isFieldPresent(sfAuctionSlot))
869 {
870 auto const& auctionSlot =
871 safeDowncast<STObject const&>(amm->peekAtField(sfAuctionSlot));
872 if (auctionSlot.isFieldPresent(sfAccount))
873 {
874 auto const slotFee = auctionSlot[~sfDiscountedFee].value_or(0);
875 auto const slotInterval = ammAuctionTimeSlot(
876 env_.app().getTimeKeeper().now().time_since_epoch().count(), auctionSlot);
877 auto const slotPrice = auctionSlot[sfPrice].iou();
878 auto const authAccounts = auctionSlot.getFieldArray(sfAuthAccounts);
879 return cb(slotFee, slotInterval, slotPrice, authAccounts);
880 }
881 }
882 }
883 return false;
884}
885
887AMM::deleteJv(AccountID const& account, Asset const& asset1, Asset const& asset2)
888{
889 json::Value jv;
890 jv[jss::Account] = to_string(account);
891 jv[jss::Asset] = STIssue(sfAsset, asset1).getJson(JsonOptions::Values::None);
892 jv[jss::Asset2] = STIssue(sfAsset, asset2).getJson(JsonOptions::Values::None);
893
894 jv[jss::TransactionType] = jss::AMMDelete;
895
896 return jv;
897}
898
899void
900AMM::ammDelete(AccountID const& account, std::optional<Ter> const& ter)
901{
902 json::Value jv = deleteJv(account, asset1_.asset(), asset2_.asset());
903 if (fee_ != 0)
904 jv[jss::Fee] = std::to_string(fee_);
905 submit(jv, std::nullopt, ter);
906}
907
908namespace amm {
909
912 Account const& issuer,
913 Account const& holder,
914 Asset const& asset,
915 Asset const& asset2,
916 std::optional<STAmount> const& amount)
917{
918 json::Value jv;
919 jv[jss::TransactionType] = jss::AMMClawback;
920 jv[jss::Account] = issuer.human();
921 jv[jss::Holder] = holder.human();
922 jv[jss::Asset] = toJson(asset);
923 jv[jss::Asset2] = toJson(asset2);
924 if (amount)
925 jv[jss::Amount] = amount->getJson(JsonOptions::Values::None);
926
927 return jv;
928}
929} // namespace amm
930} // namespace xrpl::test::jtx
T all_of(T... args)
Represents a JSON value.
Definition json_value.h:117
std::string toStyledString() const
Value & append(Value const &value)
Append value to array at the end.
bool isMember(char const *key) const
Return true if the object has a member named key.
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:26
IOUAmount iou() const
Definition STAmount.cpp:286
Asset const & asset() const
Definition STAmount.h:496
void setJson(json::Value &) const
Definition STAmount.cpp:606
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
Definition STAmount.cpp:734
XRPAmount xrp() const
Definition STAmount.cpp:271
json::Value getJson(JsonOptions) const override
Definition STIssue.cpp:88
bool expectTradingFee(std::uint16_t fee) const
Definition AMM.cpp:337
bool expectAmmRpcInfo(STAmount const &asset1, STAmount const &asset2, IOUAmount const &balance, std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt) const
Definition AMM.cpp:351
json::Value bid(BidArg const &arg)
Definition AMM.cpp:734
IOUAmount getLPTokensBalance(std::optional< AccountID > const &account=std::nullopt) const
Definition AMM.cpp:282
std::optional< IOUAmount > bidMax_
static json::Value withdrawJv(WithdrawArg const &arg)
Definition AMM.cpp:554
AccountID create(std::uint32_t tfee=0, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< jtx::Seq > const &seq=std::nullopt, std::optional< Ter > const &ter=std::nullopt)
Definition AMM.cpp:153
AccountID const ammAccount_
bool ammExists() const
Definition AMM.cpp:344
static json::Value deleteJv(AccountID const &account, Asset const &asset1, Asset const &assets)
Definition AMM.cpp:887
bool expectAuctionSlot(std::uint32_t fee, std::optional< std::uint8_t > timeSlot, IOUAmount expectedPrice) const
Definition AMM.cpp:307
IOUAmount tokens() const
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
IOUAmount withdraw(std::optional< Account > const &account, std::optional< LPToken > const &tokens, std::optional< STAmount > const &asset1OutDetails=std::nullopt, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< Ter > const &ter=std::nullopt)
Definition AMM.cpp:604
Account const creatorAccount_
std::uint32_t const fee_
void submit(json::Value const &jv, std::optional< jtx::Seq > const &seq, std::optional< Ter > const &ter)
Definition AMM.cpp:815
void ammDelete(AccountID const &account, std::optional< Ter > const &ter=std::nullopt)
Definition AMM.cpp:900
json::Value ammRpcInfo(std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< Asset > const &asset1=std::nullopt, std::optional< Asset > const &asset2=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt, bool ignoreParams=false, unsigned apiVersion=rpc::kApiInvalidVersion) const
Send amm_info RPC command.
Definition AMM.cpp:183
AccountID const & ammAccount() const
std::optional< Msig > const msig_
static json::Value createJv(AccountID const &account, STAmount const &asset1, STAmount const &asset2, std::uint16_t const &tfee)
Definition AMM.cpp:136
void setTokens(json::Value &jv, std::optional< std::pair< Asset, Asset > > const &assets=std::nullopt)
Definition AMM.cpp:392
void vote(std::optional< Account > const &account, std::uint32_t feeVal, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< jtx::Seq > const &seq=std::nullopt, std::optional< std::pair< Asset, Asset > > const &assets=std::nullopt, std::optional< Ter > const &ter=std::nullopt)
Definition AMM.cpp:708
static json::Value voteJv(VoteArg const &arg)
Definition AMM.cpp:690
std::optional< IOUAmount > bidMin_
bool expectLPTokens(AccountID const &account, IOUAmount const &tokens) const
Definition AMM.cpp:296
bool expectAmmInfo(STAmount const &asset1, STAmount const &asset2, IOUAmount const &balance, json::Value const &jv) const
Definition AMM.cpp:364
IOUAmount initialTokens()
Definition AMM.cpp:52
AMM(Env &env, Account account, STAmount asset1, STAmount asset2, bool log=false, std::uint16_t tfee=0, std::uint32_t fee=0, std::optional< std::uint32_t > flags=std::nullopt, std::optional< jtx::Seq > seq=std::nullopt, std::optional< jtx::Msig > ms=std::nullopt, std::optional< Ter > const &ter=std::nullopt, bool close=true)
Definition AMM.cpp:62
void clawback(ClawbackArg const &arg)
Definition AMM.cpp:799
std::tuple< STAmount, STAmount, STAmount > balances(Asset const &asset1, Asset const &asset2, std::optional< AccountID > const &account=std::nullopt) const
Get AMM balances for the token pair.
Definition AMM.cpp:246
static json::Value depositJv(DepositArg const &arg)
Definition AMM.cpp:407
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
IOUAmount const initialLPTokens_
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
Definition Env.h:161
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
T empty(T... args)
T end(T... args)
T find(T... args)
T holds_alternative(T... args)
T make_pair(T... args)
@ Array
array value (ordered list)
Definition json_value.h:28
@ Null
'null' value
Definition json_value.h:22
STL namespace.
Keylet computation functions.
Definition Indexes.h:40
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:441
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
static constexpr auto kApiInvalidVersion
Definition ApiVersion.h:41
json::Value ammClawback(Account const &issuer, Account const &holder, Asset const &asset, Asset const &asset2, std::optional< STAmount > const &amount)
Definition AMM.cpp:911
static Number number(STAmount const &a)
Definition AMM.cpp:44
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
STAmount ammLPHolds(ReadView const &view, Asset const &asset1, Asset const &asset2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
Dest safeDowncast(Src *s) noexcept
Definition safe_cast.h:84
constexpr FlagValue tfDepositSubTx
Definition TxFlags.h:409
bool isXRP(AccountID const &c)
Definition AccountID.h:84
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Asset const &asset1, Asset const &asset2, FreezeHandling freezeHandling, AuthHandling authHandling, beast::Journal const j)
Get AMM pool balances.
json::Value toJson(Asset const &asset)
Definition Asset.h:168
bool amountFromJsonNoThrow(STAmount &result, json::Value const &jvSource)
Number root2(Number f)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
std::optional< std::uint8_t > ammAuctionTimeSlot(std::uint64_t current, STObject const &auctionSlot)
Get time slot of the auction slot.
Definition AMMCore.cpp:111
Issue ammLPTIssue(Asset const &asset1, Asset const &asset2, AccountID const &ammAccountID)
Calculate LPT Issue from AMM asset pair.
Definition AMMCore.cpp:54
constexpr FlagValue tfWithdrawSubTx
Definition TxFlags.h:407
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
@ tesSUCCESS
Definition TER.h:245
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
STAmount toSTAmount(IOUAmount const &iou, Asset const &asset)
std::optional< Account > account
std::optional< std::variant< int, IOUAmount, STAmount > > bidMin
std::vector< Account > authAccounts
std::optional< std::variant< int, IOUAmount, STAmount > > bidMax
std::optional< std::uint32_t > flags
std::optional< std::pair< Asset, Asset > > assets
std::optional< STAmount > amount
std::optional< std::uint32_t > flags
std::optional< std::pair< Asset, Asset > > assets
std::optional< std::uint32_t > flags
std::optional< std::uint16_t > tfee
std::optional< LPToken > tokens
std::optional< STAmount > maxEP
std::optional< std::pair< Asset, Asset > > assets
std::optional< jtx::Seq > seq
std::optional< Account > account
std::optional< STAmount > asset2In
std::optional< STAmount > asset1In
std::optional< std::pair< Asset, Asset > > assets
std::optional< Ter > err
std::optional< jtx::Seq > seq
std::optional< Account > account
std::optional< std::uint32_t > flags
std::optional< STAmount > asset2Out
std::optional< jtx::Seq > seq
std::optional< std::pair< Asset, Asset > > assets
std::optional< LPToken > maxEP
std::optional< STAmount > asset1Out
std::optional< std::uint32_t > flags
std::optional< Account > account
std::optional< LPToken > tokens
T swap(T... args)
T to_string(T... args)