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 for (auto const& account : accounts)
330 {
331 if (std::ranges::find(authAccounts, account.getAccountID(sfAccount)) ==
332 authAccounts.end())
333 return false;
334 }
335 return true;
336 });
337}
338
339bool
341{
342 auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset()));
343 return amm && (*amm)[sfTradingFee] == fee;
344}
345
346bool
348{
349 return env_.current()->read(keylet::account(ammAccount_)) != nullptr &&
350 env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())) != nullptr;
351}
352
353bool
355 STAmount const& asset1,
356 STAmount const& asset2,
357 IOUAmount const& balance,
358 std::optional<AccountID> const& account,
359 std::optional<std::string> const& ledgerIndex,
361{
362 auto const jv = ammRpcInfo(account, ledgerIndex, std::nullopt, std::nullopt, ammAccount);
363 return expectAmmInfo(asset1, asset2, balance, jv);
364}
365
366bool
368 STAmount const& asset1,
369 STAmount const& asset2,
370 IOUAmount const& balance,
371 json::Value const& jvRes) const
372{
373 if (!jvRes.isMember(jss::amm))
374 return false;
375 auto const& jv = jvRes[jss::amm];
376 if (!jv.isMember(jss::amount) || !jv.isMember(jss::amount2) || !jv.isMember(jss::lp_token))
377 return false;
378 STAmount asset1Info;
379 if (!amountFromJsonNoThrow(asset1Info, jv[jss::amount]))
380 return false;
381 STAmount asset2Info;
382 if (!amountFromJsonNoThrow(asset2Info, jv[jss::amount2]))
383 return false;
384 STAmount lptBalance;
385 if (!amountFromJsonNoThrow(lptBalance, jv[jss::lp_token]))
386 return false;
387 // ammRpcInfo returns unordered assets
388 if (asset1Info.asset() != asset1.asset())
389 std::swap(asset1Info, asset2Info);
390 return asset1 == asset1Info && asset2 == asset2Info &&
391 lptBalance == STAmount{balance, lptIssue_};
392}
393
394void
396{
397 if (assets)
398 {
399 jv[jss::Asset] = STIssue(sfAsset, assets->first).getJson(JsonOptions::Values::None);
400 jv[jss::Asset2] = STIssue(sfAsset, assets->second).getJson(JsonOptions::Values::None);
401 }
402 else
403 {
404 jv[jss::Asset] = STIssue(sfAsset, asset1_.asset()).getJson(JsonOptions::Values::None);
405 jv[jss::Asset2] = STIssue(sfAsset, asset2_.asset()).getJson(JsonOptions::Values::None);
406 }
407}
408
411{
412 json::Value jv;
413 if (!arg.account || !arg.assets)
414 Throw<std::runtime_error>("AMM::depositJv: account or assets not set");
415
416 jv[jss::Account] = arg.account->human();
417 jv[jss::Asset] = STIssue(sfAsset, arg.assets->first).getJson(JsonOptions::Values::None);
418 jv[jss::Asset2] = STIssue(sfAsset, arg.assets->second).getJson(JsonOptions::Values::None);
419 if (arg.tokens)
420 arg.tokens->tokens().setJson(jv[jss::LPTokenOut]);
421 if (arg.asset1In)
422 arg.asset1In->setJson(jv[jss::Amount]);
423 if (arg.asset2In)
424 arg.asset2In->setJson(jv[jss::Amount2]);
425 if (arg.maxEP)
426 arg.maxEP->setJson(jv[jss::EPrice]);
427 if (arg.tfee)
428 jv[jss::TradingFee] = *arg.tfee;
429 std::uint32_t flags = 0;
430 if (arg.flags)
431 flags = *arg.flags;
432 // If including asset1In and asset2In or tokens as
433 // deposit min amounts then must set the flags
434 // explicitly instead of relying on this logic.
435 if ((flags & tfDepositSubTx) == 0u)
436 {
437 if (arg.tokens && !arg.asset1In)
438 {
439 flags |= tfLPToken;
440 }
441 else if (arg.tokens && arg.asset1In)
442 {
443 flags |= tfOneAssetLPToken;
444 }
445 else if (arg.asset1In && arg.asset2In)
446 {
447 flags |= tfTwoAsset;
448 }
449 else if (arg.maxEP && arg.asset1In)
450 {
451 flags |= tfLimitLPToken;
452 }
453 else if (arg.asset1In)
454 {
455 flags |= tfSingleAsset;
456 }
457 }
458 jv[jss::Flags] = flags;
459
460 jv[jss::TransactionType] = jss::AMMDeposit;
461
462 return jv;
463}
464
467 std::optional<Account> const& account,
469 std::optional<STAmount> const& asset1In,
470 std::optional<std::uint32_t> const& flags,
471 std::optional<Ter> const& ter)
472{
473 return deposit(
474 account,
475 tokens,
476 asset1In,
477 std::nullopt,
478 std::nullopt,
479 flags,
480 std::nullopt,
481 std::nullopt,
482 std::nullopt,
483 ter);
484}
485
488 std::optional<Account> const& account,
489 STAmount const& asset1In,
490 std::optional<STAmount> const& asset2In,
491 std::optional<STAmount> const& maxEP,
492 std::optional<std::uint32_t> const& flags,
493 std::optional<Ter> const& ter)
494{
495 if (asset2In && maxEP)
496 Throw<std::runtime_error>("Invalid options: asset2In and maxEP");
497 return deposit(
498 account,
499 std::nullopt,
500 asset1In,
501 asset2In,
502 maxEP,
503 flags,
504 std::nullopt,
505 std::nullopt,
506 std::nullopt,
507 ter);
508}
509
512 std::optional<Account> const& account,
514 std::optional<STAmount> const& asset1In,
515 std::optional<STAmount> const& asset2In,
516 std::optional<STAmount> const& maxEP,
517 std::optional<std::uint32_t> const& flags,
519 std::optional<jtx::Seq> const& seq,
521 std::optional<Ter> const& ter)
522{
523 auto const acct = account ? account : creatorAccount_;
524 auto const lpTokens = getLPTokensBalance(acct);
526 {.account = acct,
527 .tokens = tokens ? tokens->tokens(lptIssue_) : tokens,
528 .asset1In = asset1In,
529 .asset2In = asset2In,
530 .maxEP = maxEP,
531 .flags = flags,
532 .assets = assets ? assets : std::make_pair(asset1_.asset(), asset2_.asset()),
533 .tfee = tfee});
534 if (fee_ != 0)
535 jv[jss::Fee] = std::to_string(fee_);
536 submit(jv, seq, ter);
537 return getLPTokensBalance(acct) - lpTokens;
538}
539
542{
543 return deposit(
544 arg.account,
545 arg.tokens,
546 arg.asset1In,
547 arg.asset2In,
548 arg.maxEP,
549 arg.flags,
550 arg.assets,
551 arg.seq,
552 arg.tfee,
553 arg.err);
554}
555
558{
559 json::Value jv;
560 if (!arg.account || !arg.assets)
561 Throw<std::runtime_error>("AMM::withdrawJv: account or assets not set");
562 jv[jss::Account] = arg.account->human();
563 jv[jss::Asset] = STIssue(sfAsset, arg.assets->first).getJson(JsonOptions::Values::None);
564 jv[jss::Asset2] = STIssue(sfAsset, arg.assets->second).getJson(JsonOptions::Values::None);
565 if (arg.tokens)
566 arg.tokens->tokens().setJson(jv[jss::LPTokenIn]);
567 if (arg.asset1Out)
568 arg.asset1Out->setJson(jv[jss::Amount]);
569 if (arg.asset2Out)
570 arg.asset2Out->setJson(jv[jss::Amount2]);
571 if (arg.maxEP)
572 arg.maxEP->tokens().setJson(jv[jss::EPrice]);
573 std::uint32_t flags = 0;
574 if (arg.flags)
575 flags = *arg.flags;
576 if ((flags & tfWithdrawSubTx) == 0u)
577 {
578 if (arg.tokens && !arg.asset1Out)
579 {
580 flags |= tfLPToken;
581 }
582 else if (arg.asset1Out && arg.asset2Out)
583 {
584 flags |= tfTwoAsset;
585 }
586 else if (arg.tokens && arg.asset1Out)
587 {
588 flags |= tfOneAssetLPToken;
589 }
590 else if (arg.asset1Out && arg.maxEP)
591 {
592 flags |= tfLimitLPToken;
593 }
594 else if (arg.asset1Out)
595 {
596 flags |= tfSingleAsset;
597 }
598 }
599 jv[jss::Flags] = flags;
600
601 jv[jss::TransactionType] = jss::AMMWithdraw;
602
603 return jv;
604}
605
608 std::optional<Account> const& account,
610 std::optional<STAmount> const& asset1Out,
611 std::optional<std::uint32_t> const& flags,
612 std::optional<Ter> const& ter)
613{
614 return withdraw(
615 account,
616 tokens,
617 asset1Out,
618 std::nullopt,
619 std::nullopt,
620 flags,
621 std::nullopt,
622 std::nullopt,
623 ter);
624}
625
628 std::optional<Account> const& account,
629 STAmount const& asset1Out,
630 std::optional<STAmount> const& asset2Out,
631 std::optional<LPToken> const& maxEP,
632 std::optional<Ter> const& ter)
633{
634 if (asset2Out && maxEP)
635 Throw<std::runtime_error>("Invalid options: asset2Out and maxEP");
636 return withdraw(
637 account,
638 std::nullopt,
639 asset1Out,
640 asset2Out,
641 maxEP,
642 std::nullopt,
643 std::nullopt,
644 std::nullopt,
645 ter);
646}
647
650 std::optional<Account> const& account,
652 std::optional<STAmount> const& asset1Out,
653 std::optional<STAmount> const& asset2Out,
654 std::optional<LPToken> const& maxEP,
655 std::optional<std::uint32_t> const& flags,
657 std::optional<jtx::Seq> const& seq,
658 std::optional<Ter> const& ter)
659{
660 auto const acct = account ? account : creatorAccount_;
661 auto const lpTokens = getLPTokensBalance(acct);
663 .account = acct,
664 .tokens = tokens ? tokens->tokens(lptIssue_) : tokens,
665 .asset1Out = asset1Out,
666 .asset2Out = asset2Out,
667 .maxEP = maxEP ? maxEP->tokens(lptIssue_) : maxEP,
668 .flags = flags,
669 .assets = assets ? assets : std::make_pair(asset1_.asset(), asset2_.asset()),
670 });
671 if (fee_ != 0)
672 jv[jss::Fee] = std::to_string(fee_);
673 submit(jv, seq, ter);
674 return lpTokens - getLPTokensBalance(acct);
675}
676
679{
680 return withdraw(
681 arg.account,
682 arg.tokens,
683 arg.asset1Out,
684 arg.asset2Out,
685 arg.maxEP,
686 arg.flags,
687 arg.assets,
688 arg.seq,
689 arg.err);
690}
691
694{
695 json::Value jv;
696 if (!arg.account || !arg.assets)
697 Throw<std::runtime_error>("AMM::withdrawJv: account or assets not set");
698 jv[jss::Account] = arg.account->human();
699 jv[jss::Asset] = STIssue(sfAsset, arg.assets->first).getJson(JsonOptions::Values::None);
700 jv[jss::Asset2] = STIssue(sfAsset, arg.assets->second).getJson(JsonOptions::Values::None);
701 jv[jss::TradingFee] = arg.tfee;
702 if (arg.flags)
703 jv[jss::Flags] = *arg.flags;
704
705 jv[jss::TransactionType] = jss::AMMVote;
706
707 return jv;
708}
709
710void
712 std::optional<Account> const& account,
713 std::uint32_t feeVal,
714 std::optional<std::uint32_t> const& flags,
715 std::optional<jtx::Seq> const& seq,
717 std::optional<Ter> const& ter)
718{
719 json::Value jv = voteJv({
720 .account = account ? account : creatorAccount_,
721 .tfee = feeVal,
722 .flags = flags,
723 .assets = assets ? assets : std::make_pair(asset1_.asset(), asset2_.asset()),
724 });
725 if (fee_ != 0)
726 jv[jss::Fee] = std::to_string(fee_);
727 submit(jv, seq, ter);
728}
729
730void
732{
733 vote(arg.account, arg.tfee, arg.flags, arg.seq, arg.assets, arg.err);
734}
735
737AMM::bid(BidArg const& arg)
738{
739 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
740 {
741 if (env_.current()->rules().enabled(fixInnerObjTemplate) &&
742 !amm->isFieldPresent(sfAuctionSlot))
743 Throw<std::runtime_error>("AMM::Bid");
744 if (amm->isFieldPresent(sfAuctionSlot))
745 {
746 auto const& auctionSlot =
747 safeDowncast<STObject const&>(amm->peekAtField(sfAuctionSlot));
748 lastPurchasePrice_ = auctionSlot[sfPrice].iou();
749 }
750 }
751 bidMin_ = std::nullopt;
752 bidMax_ = std::nullopt;
753
754 json::Value jv;
755 jv[jss::Account] = arg.account ? arg.account->human() : creatorAccount_.human();
756 setTokens(jv, arg.assets);
757 auto getBid = [&](auto const& bid) {
759 {
760 return STAmount{lptIssue_, std::get<int>(bid)};
761 }
763 {
764 return toSTAmount(std::get<IOUAmount>(bid), lptIssue_);
765 }
766
767 return std::get<STAmount>(bid);
768 };
769 if (arg.bidMin)
770 {
771 STAmount const saTokens = getBid(*arg.bidMin);
772 saTokens.setJson(jv[jss::BidMin]);
773 bidMin_ = saTokens.iou();
774 }
775 if (arg.bidMax)
776 {
777 STAmount const saTokens = getBid(*arg.bidMax);
778 saTokens.setJson(jv[jss::BidMax]);
779 bidMax_ = saTokens.iou();
780 }
781 if (!arg.authAccounts.empty())
782 {
784 for (auto const& account : arg.authAccounts)
785 {
786 json::Value acct;
787 json::Value authAcct;
788 acct[jss::Account] = account.human();
789 authAcct[jss::AuthAccount] = acct;
790 accounts.append(authAcct);
791 }
792 jv[jss::AuthAccounts] = accounts;
793 }
794 if (arg.flags)
795 jv[jss::Flags] = *arg.flags;
796 jv[jss::TransactionType] = jss::AMMBid;
797 if (fee_ != 0)
798 jv[jss::Fee] = std::to_string(fee_);
799 return jv;
800}
801
802void
804{
805 auto const& [asset, asset2] = [&]() {
806 if (arg.assets)
807 return *arg.assets;
808 return std::make_pair(asset1_.asset(), asset2_.asset());
809 }();
810 auto jv = amm::ammClawback(arg.issuer, arg.holder, asset, asset2, arg.amount);
811 if (arg.flags)
812 jv[jss::Flags] = *arg.flags;
813 if (fee_ != 0)
814 jv[jss::Fee] = std::to_string(fee_);
815 submit(jv, std::nullopt, arg.err);
816}
817
818void
820 json::Value const& jv,
821 std::optional<jtx::Seq> const& seq,
822 std::optional<Ter> const& ter)
823{
824 if (log_)
826 if (msig_)
827 {
828 if (seq && ter)
829 {
830 env_(jv, *msig_, *seq, *ter);
831 }
832 else if (seq)
833 {
834 env_(jv, *msig_, *seq);
835 }
836 else if (ter)
837 {
838 env_(jv, *msig_, *ter);
839 }
840 else
841 {
842 env_(jv, *msig_);
843 }
844 }
845 else if (seq && ter)
846 {
847 env_(jv, *seq, *ter);
848 }
849 else if (seq)
850 {
851 env_(jv, *seq);
852 }
853 else if (ter)
854 {
855 env_(jv, *ter);
856 }
857 else
858 {
859 env_(jv);
860 }
861 if (doClose_)
862 env_.close();
863}
864
865bool
867{
868 if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
869 {
870 if (env_.current()->rules().enabled(fixInnerObjTemplate) &&
871 !amm->isFieldPresent(sfAuctionSlot))
872 Throw<std::runtime_error>("AMM::expectAuctionSlot");
873 if (amm->isFieldPresent(sfAuctionSlot))
874 {
875 auto const& auctionSlot =
876 safeDowncast<STObject const&>(amm->peekAtField(sfAuctionSlot));
877 if (auctionSlot.isFieldPresent(sfAccount))
878 {
879 // This could fail in pre-fixInnerObjTemplate tests
880 // if the submitted transactions recreate one of
881 // the failure scenarios. Access as optional
882 // to avoid the failure.
883 auto const slotFee = auctionSlot[~sfDiscountedFee].value_or(0);
884 auto const slotInterval = ammAuctionTimeSlot(
885 env_.app().getTimeKeeper().now().time_since_epoch().count(), auctionSlot);
886 auto const slotPrice = auctionSlot[sfPrice].iou();
887 auto const authAccounts = auctionSlot.getFieldArray(sfAuthAccounts);
888 return cb(slotFee, slotInterval, slotPrice, authAccounts);
889 }
890 }
891 }
892 return false;
893}
894
896AMM::deleteJv(AccountID const& account, Asset const& asset1, Asset const& asset2)
897{
898 json::Value jv;
899 jv[jss::Account] = to_string(account);
900 jv[jss::Asset] = STIssue(sfAsset, asset1).getJson(JsonOptions::Values::None);
901 jv[jss::Asset2] = STIssue(sfAsset, asset2).getJson(JsonOptions::Values::None);
902
903 jv[jss::TransactionType] = jss::AMMDelete;
904
905 return jv;
906}
907
908void
909AMM::ammDelete(AccountID const& account, std::optional<Ter> const& ter)
910{
911 json::Value jv = deleteJv(account, asset1_.asset(), asset2_.asset());
912 if (fee_ != 0)
913 jv[jss::Fee] = std::to_string(fee_);
914 submit(jv, std::nullopt, ter);
915}
916
917namespace amm {
918
921 Account const& issuer,
922 Account const& holder,
923 Asset const& asset,
924 Asset const& asset2,
925 std::optional<STAmount> const& amount)
926{
927 json::Value jv;
928 jv[jss::TransactionType] = jss::AMMClawback;
929 jv[jss::Account] = issuer.human();
930 jv[jss::Holder] = holder.human();
931 jv[jss::Asset] = toJson(asset);
932 jv[jss::Asset2] = toJson(asset2);
933 if (amount)
934 jv[jss::Amount] = amount->getJson(JsonOptions::Values::None);
935
936 return jv;
937}
938} // namespace amm
939} // namespace xrpl::test::jtx
Represents a JSON value.
Definition json_value.h:130
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:24
IOUAmount iou() const
Definition STAmount.cpp:286
Asset const & asset() const
Definition STAmount.h:478
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:82
bool expectTradingFee(std::uint16_t fee) const
Definition AMM.cpp:340
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:354
json::Value bid(BidArg const &arg)
Definition AMM.cpp:737
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:557
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:347
static json::Value deleteJv(AccountID const &account, Asset const &asset1, Asset const &assets)
Definition AMM.cpp:896
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:466
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:607
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:819
void ammDelete(AccountID const &account, std::optional< Ter > const &ter=std::nullopt)
Definition AMM.cpp:909
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:395
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:711
static json::Value voteJv(VoteArg const &arg)
Definition AMM.cpp:693
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:367
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:803
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
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:410
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:17
std::string const & human() const
Returns the human readable public key.
Definition jtx/Account.h:92
A transaction testing environment.
Definition Env.h:143
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
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:25
@ Null
'null' value
Definition json_value.h:19
STL namespace.
static constexpr auto kApiInvalidVersion
Definition ApiVersion.h:40
Keylet computation functions.
Definition Indexes.h:34
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:425
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
json::Value ammClawback(Account const &issuer, Account const &holder, Asset const &asset, Asset const &asset2, std::optional< STAmount > const &amount)
Definition AMM.cpp:920
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:78
constexpr FlagValue tfDepositSubTx
Definition TxFlags.h:398
bool isXRP(AccountID const &c)
Definition AccountID.h:70
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
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:157
bool amountFromJsonNoThrow(STAmount &result, json::Value const &jvSource)
Number root2(Number f)
Definition Number.cpp:1275
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
std::optional< std::uint8_t > ammAuctionTimeSlot(std::uint64_t current, STObject const &auctionSlot)
Get time slot of the auction slot.
Definition AMMCore.cpp:110
Issue ammLPTIssue(Asset const &asset1, Asset const &asset2, AccountID const &ammAccountID)
Calculate LPT Issue from AMM asset pair.
Definition AMMCore.cpp:53
constexpr FlagValue tfWithdrawSubTx
Definition TxFlags.h:396
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:240
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
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)