1#include <xrpld/app/misc/AMMHelpers.h>
2#include <xrpld/app/misc/AMMUtils.h>
3#include <xrpld/app/tx/detail/AMMDeposit.h>
5#include <xrpl/ledger/Sandbox.h>
6#include <xrpl/ledger/View.h>
7#include <xrpl/protocol/AMMCore.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/TxFlags.h>
31 auto const amount = ctx.
tx[~sfAmount];
32 auto const amount2 = ctx.
tx[~sfAmount2];
33 auto const ePrice = ctx.
tx[~sfEPrice];
34 auto const lpTokens = ctx.
tx[~sfLPTokenOut];
35 auto const tradingFee = ctx.
tx[~sfTradingFee];
45 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid flags.";
51 if (!lpTokens || ePrice || (amount && !amount2) || (!amount && amount2) || tradingFee)
57 if (!amount || amount2 || ePrice || tradingFee)
63 if (!amount || !amount2 || ePrice || tradingFee)
68 if (!amount || !lpTokens || amount2 || ePrice || tradingFee)
73 if (!amount || !ePrice || lpTokens || amount2 || tradingFee)
78 if (!amount || !amount2 || ePrice || lpTokens)
82 auto const asset = ctx.
tx[sfAsset].get<
Issue>();
83 auto const asset2 = ctx.
tx[sfAsset2].get<
Issue>();
86 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid asset pair.";
90 if (amount && amount2 && amount->issue() == amount2->issue())
92 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid tokens, same issue." << amount->issue() <<
" " << amount2->issue();
96 if (lpTokens && *lpTokens <= beast::zero)
98 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid LPTokens";
107 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid amount";
116 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid amount2";
122 if (amount && ePrice)
127 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid EPrice";
134 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid trading fee.";
144 auto const accountID = ctx.
tx[sfAccount];
149 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: Invalid asset pair.";
153 auto const expected =
156 return expected.error();
157 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
160 if (lptAMMBalance != beast::zero)
162 if (amountBalance != beast::zero || amount2Balance != beast::zero)
165 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: tokens balance is not zero.";
172 if (lptAMMBalance == beast::zero)
174 if (amountBalance <= beast::zero || amount2Balance <= beast::zero || lptAMMBalance < beast::zero)
177 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: reserves or tokens balance is zero.";
188 auto balance = [&](
auto const&
deposit) ->
TER {
191 auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue();
200 return (accountID ==
deposit.issue().account ||
210 auto checkAsset = [&](
Issue const& asset) ->
TER {
213 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: account is not authorized, " << asset;
219 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: account or currency is frozen, " <<
to_string(accountID) <<
" "
228 if (
auto const ter = checkAsset(ctx.
tx[sfAsset].get<
Issue>()))
231 if (
auto const ter = checkAsset(ctx.
tx[sfAsset2].get<
Issue>()))
235 auto const amount = ctx.
tx[~sfAmount];
236 auto const amount2 = ctx.
tx[~sfAmount2];
237 auto const ammAccountID = ammSle->
getAccountID(sfAccount);
245 if (
auto const ter =
requireAuth(ctx.
view, amount->issue(), accountID))
248 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: account is not authorized, " << amount->issue();
255 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: AMM account or currency is frozen, " <<
to_string(accountID);
261 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: account is frozen, " <<
to_string(accountID) <<
" "
267 if (
auto const ter = balance(*amount))
269 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: account has insufficient funds, " << *amount;
280 if (
auto const ter = checkAmount(amount,
true))
283 if (
auto const ter = checkAmount(amount2,
true))
288 if (
auto const ter = checkAmount(amountBalance,
false))
290 if (
auto const ter = checkAmount(amount2Balance,
false))
295 if (
auto const lpTokens = ctx.
tx[~sfLPTokenOut]; lpTokens && lpTokens->issue() != lptAMMBalance.issue())
297 JLOG(ctx.
j.
debug()) <<
"AMM Deposit: invalid LPTokens.";
307 if (xrpBalance <= beast::zero)
309 JLOG(ctx.
j.
debug()) <<
"AMM Instance: insufficient reserves";
320 auto const amount =
ctx_.
tx[~sfAmount];
321 auto const amount2 =
ctx_.
tx[~sfAmount2];
322 auto const ePrice =
ctx_.
tx[~sfEPrice];
323 auto const lpTokensDeposit =
ctx_.
tx[~sfLPTokenOut];
327 auto const ammAccountID = (*ammSle)[sfAccount];
337 return {expected.error(),
false};
338 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
339 auto const tfee = (lptAMMBalance == beast::zero) ?
ctx_.
tx[~sfTradingFee].value_or(0)
344 auto const [result, newLPTokenBalance] = [&,
345 &amountBalance = amountBalance,
346 &amount2Balance = amount2Balance,
360 return singleDepositTokens(sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *lpTokensDeposit, tfee);
362 return singleDepositEPrice(sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *ePrice, tfee);
364 return singleDeposit(sb, ammAccountID, amountBalance, lptAMMBalance, *amount, lpTokensDeposit, tfee);
380 JLOG(
j_.
error()) <<
"AMM Deposit: invalid options.";
387 XRPL_ASSERT(newLPTokenBalance > beast::zero,
"xrpl::AMMDeposit::applyGuts : valid new LP token balance");
388 ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
391 if (lptAMMBalance == beast::zero)
430 auto checkBalance = [&](
auto const& depositAmount) ->
TER {
431 if (depositAmount <= beast::zero)
433 if (
isXRP(depositAmount))
435 auto const& lpIssue = lpTokensDeposit.
issue();
442 account_ == depositAmount.issue().account ||
450 amountBalance, amountDeposit, amount2Deposit, lptAMMBalance, lpTokensDeposit, tfee,
IsDeposit::Yes);
452 if (lpTokensDepositActual <= beast::zero)
458 if (amountDepositActual < depositMin || amount2DepositActual < deposit2Min ||
459 lpTokensDepositActual < lpTokensDepositMin)
461 JLOG(
ctx_.
journal.
debug()) <<
"AMM Deposit: min deposit fails " << amountDepositActual <<
" "
463 << amount2DepositActual.value_or(
STAmount{}) <<
" "
470 if (
auto const ter = checkBalance(amountDepositActual))
473 "checkBalance to deposit or is 0"
474 << amountDepositActual;
481 JLOG(
ctx_.
journal.
debug()) <<
"AMM Deposit: failed to deposit " << amountDepositActual;
486 if (amount2DepositActual)
488 if (
auto const ter = checkBalance(*amount2DepositActual))
490 JLOG(
ctx_.
journal.
debug()) <<
"AMM Deposit: account has insufficient checkBalance to "
492 << *amount2DepositActual;
499 JLOG(
ctx_.
journal.
debug()) <<
"AMM Deposit: failed to deposit " << *amount2DepositActual;
512 return {
tesSUCCESS, lptAMMBalance + lpTokensDepositActual};
518 if (!rules.
enabled(fixAMMv1_3))
519 return lpTokensDeposit;
543 auto const frac =
divide(tokensAdj, lptAMMBalance, lptAMMBalance.
issue());
563 JLOG(
j_.
error()) <<
"AMMDeposit::equalDepositTokens exception " << e.
what();
609 auto frac =
Number{amount} / amountBalance;
611 if (tokensAdj == beast::zero)
621 if (amount2Deposit <= amount2)
634 frac =
Number{amount2} / amount2Balance;
636 if (tokensAdj == beast::zero)
646 if (amountDeposit <= amount)
682 if (tokens == beast::zero)
690 auto const [tokensAdj, amountDepositAdj] =
729 auto const amountDeposit =
ammAssetIn(amountBalance, lptAMMBalance, tokensAdj, tfee);
730 if (amountDeposit > amount)
781 if (amount != beast::zero)
785 if (tokens <= beast::zero)
793 auto const [tokensAdj, amountDepositAdj] =
797 auto const ep =
Number{amountDepositAdj} / tokensAdj;
832 auto const c = f1 * amountBalance / (ePrice * lptAMMBalance);
833 auto const d = f1 + c * f2 - c;
834 auto const a1 = c * c;
835 auto const b1 = c * c * f2 * f2 + 2 * c - d * d;
836 auto const c1 = 2 * c * f2 * f2 + 1 - 2 * d * f2;
837 auto amtNoRoundCb = [&] {
return f1 * amountBalance *
solveQuadraticEq(a1, b1, c1); };
840 if (amountDeposit <= beast::zero)
842 auto tokNoRoundCb = [&] {
return amountDeposit / ePrice; };
843 auto tokProdCb = [&] {
return amountDeposit / ePrice; };
846 auto const [tokensAdj, amountDepositAdj] =
871 Issue const& lptIssue,
static TER preclaim(PreclaimContext const &ctx)
std::pair< TER, STAmount > equalDepositInEmptyState(Sandbox &view, AccountID const &ammAccount, STAmount const &amount, STAmount const &amount2, Issue const &lptIssue, std::uint16_t tfee)
Equal deposit in empty AMM state (LP tokens balance is 0)
static bool checkExtraFeatures(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
std::pair< TER, STAmount > singleDepositTokens(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit, std::uint16_t tfee)
Single asset deposit (Asset1In, LPTokens) by the tokens.
std::pair< TER, STAmount > equalDepositTokens(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit, std::optional< STAmount > const &depositMin, std::optional< STAmount > const &deposit2Min, std::uint16_t tfee)
Equal asset deposit (LPTokens) for the specified share of the AMM instance pools.
std::pair< TER, STAmount > singleDepositEPrice(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &ePrice, std::uint16_t tfee)
Single asset deposit (Asset1In, EPrice) with two constraints.
std::pair< TER, bool > applyGuts(Sandbox &view)
std::pair< TER, STAmount > singleDeposit(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, std::optional< STAmount > const &lpTokensDepositMin, std::uint16_t tfee)
Single asset deposit (Asset1In) by the amount.
static NotTEC preflight(PreflightContext const &ctx)
std::pair< TER, STAmount > equalDepositLimit(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &amount2, std::optional< STAmount > const &lpTokensDepositMin, std::uint16_t tfee)
Equal asset deposit (Asset1In, Asset2In) with the constraint on the maximum amount of both assets tha...
std::pair< TER, STAmount > deposit(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amountDeposit, std::optional< STAmount > const &amount2Deposit, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit, std::optional< STAmount > const &depositMin, std::optional< STAmount > const &deposit2Min, std::optional< STAmount > const &lpTokensDepositMin, std::uint16_t tfee)
Deposit requested assets and token amount into LP account.
beast::Journal const journal
A currency issued by an account.
Number is a floating point type that can represent a wide range of values.
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
Rules controlling protocol behavior.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Issue const & issue() const
AccountID getAccountID(SField const &field) const
std::uint32_t getFlags() const
Discardable, editable view to a ledger.
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
T make_optional(T... args)
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
STAmount divide(STAmount const &amount, Rate const &rate)
std::uint16_t constexpr TRADING_FEE_THRESHOLD
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
static STAmount adjustLPTokensOut(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit)
Number feeMultHalf(std::uint16_t tfee)
Get fee multiplier (1 - tfee / 2) @tfee trading fee in basis points.
constexpr std::uint32_t tfDepositMask
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
bool isXRP(AccountID const &c)
constexpr std::uint32_t tfSingleAsset
Number adjustFracByTokens(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &tokens, Number const &frac)
Find a fraction of tokens after the tokens are adjusted.
std::string to_string(base_uint< Bits, Tag > const &a)
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
Number solveQuadraticEq(Number const &a, Number const &b, Number const &c)
Positive solution for quadratic equation: x = (-b + sqrt(b**2 + 4*a*c))/(2*a)
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
constexpr std::uint32_t tfTwoAsset
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
constexpr std::uint32_t tfOneAssetLPToken
constexpr std::uint32_t tfDepositSubTx
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
std::pair< STAmount, STAmount > adjustAssetInByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
Expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Issue > const &optIssue1, std::optional< Issue > const &optIssue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool and LP token balances.
TERSubset< CanCvtToTER > TER
constexpr std::uint32_t tfTwoAssetIfEmpty
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
std::tuple< STAmount, std::optional< STAmount >, STAmount > adjustAmountsByLPTokens(STAmount const &amountBalance, STAmount const &amount, std::optional< STAmount > const &amount2, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee, IsDeposit isDeposit)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
constexpr std::uint32_t tfLimitLPToken
constexpr std::uint32_t tfLPToken
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
STAmount lpTokensOut(STAmount const &asset1Balance, STAmount const &asset1Deposit, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's deposit amount.
STAmount ammLPHolds(ReadView const &view, Currency const &cur1, Currency const &cur2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.