1#include <xrpl/tx/transactors/dex/AMMClawback.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/Sandbox.h>
8#include <xrpl/ledger/helpers/AMMHelpers.h>
9#include <xrpl/ledger/helpers/TokenHelpers.h>
10#include <xrpl/protocol/AccountID.h>
11#include <xrpl/protocol/AmountConversions.h>
12#include <xrpl/protocol/Asset.h>
13#include <xrpl/protocol/Feature.h>
14#include <xrpl/protocol/IOUAmount.h>
15#include <xrpl/protocol/Indexes.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/LedgerFormats.h>
18#include <xrpl/protocol/MPTIssue.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/STLedgerEntry.h>
22#include <xrpl/protocol/STTx.h>
23#include <xrpl/protocol/TER.h>
24#include <xrpl/protocol/TxFlags.h>
25#include <xrpl/protocol/XRPAmount.h>
26#include <xrpl/tx/Transactor.h>
27#include <xrpl/tx/transactors/dex/AMMWithdraw.h>
38 return tfAMMClawbackMask;
50 (!(clawAmount && clawAmount->holds<
MPTIssue>()) && !ctx.
tx[sfAsset].holds<
MPTIssue>() &&
62 JLOG(ctx.
j.
trace()) <<
"AMMClawback: holder cannot be the same as issuer.";
67 auto const asset = ctx.
tx[sfAsset];
68 auto const asset2 = ctx.
tx[sfAsset2];
73 if (ctx.
tx.
isFlag(tfClawTwoAssets) && asset.getIssuer() != asset2.getIssuer())
75 JLOG(ctx.
j.
trace()) <<
"AMMClawback: tfClawTwoAssets can only be enabled when two "
76 "assets in the AMM pool are both issued by the issuer";
80 if (asset.getIssuer() != issuer)
82 JLOG(ctx.
j.
trace()) <<
"AMMClawback: Asset's account does not "
83 "match Account field.";
87 if (clawAmount && clawAmount->asset() != asset)
89 JLOG(ctx.
j.
trace()) <<
"AMMClawback: Amount's asset subfield "
90 "does not match Asset field";
94 if (clawAmount && *clawAmount <= beast::kZero)
103 auto const asset = ctx.
tx[sfAsset];
104 auto const asset2 = ctx.
tx[sfAsset2];
115 JLOG(ctx.
j.
debug()) <<
"AMM Clawback: Invalid asset pair.";
123 if (!sleIssuer->isFlag(lsfAllowTrustLineClawback) || sleIssuer->isFlag(lsfNoFreeze))
129 auto const checkClawAsset = [&](
Asset const asset) ->
bool {
131 [&](
Issue const& issue) {
135 return sleIssuer->isFlag(lsfAllowTrustLineClawback) &&
136 !sleIssuer->isFlag(lsfNoFreeze);
141 return sleIssuance && sleIssuance->isFlag(lsfMPTCanClawback) &&
142 sleIssuance->getAccountID(sfIssuer) == ctx.
tx[sfAccount];
146 if (!checkClawAsset(asset))
149 if (ctx.
tx.
isFlag(tfClawTwoAssets) && !checkClawAsset(asset2))
180 auto const ammAccount = (*ammSle)[sfAccount];
188 auto const lpTokenBalance =
ammLPHolds(sb, *ammSle, holder,
j_);
189 if (lpTokenBalance == beast::kZero)
207 return expected.error();
208 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
217 auto const holdLPtokens =
ammLPHolds(sb, *ammSle, holder,
j_);
218 if (holdLPtokens == beast::kZero)
225 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
245 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
276 JLOG(
ctx_.journal.trace()) <<
"AMM Withdraw during AMMClawback: lptoken new balance: "
278 <<
" old balance: " <<
to_string(lptAMMBalance.iou());
280 auto sendAmount = [&](
STAmount const& saAmount) ->
TER {
281 bool const checkIssuer = saAmount.holds<
Issue>();
285 auto const ter = sendAmount(amountWithdraw);
293 if (!amount2Withdraw)
296 if (
ctx_.tx.isFlag(tfClawTwoAssets))
297 return sendAmount(*amount2Withdraw);
314 auto frac =
Number{amount} / amountBalance;
315 auto amount2Withdraw = amount2Balance * frac;
317 auto const lpTokensWithdraw =
toSTAmount(lptAMMBalance.
asset(), lptAMMBalance * frac);
318 if (lpTokensWithdraw > holdLPtokens)
341 auto const& rules = sb.
rules();
342 if (rules.enabled(fixAMMClawbackRounding))
347 if (tokensAdj == beast::kZero)
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static bool checkExtraFeatures(PreflightContext const &ctx)
TER applyGuts(Sandbox &view)
std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > equalWithdrawMatchingOneAmount(Sandbox &view, SLE const &ammSle, AccountID const &holder, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &holdLPtokens, STAmount const &amount)
Withdraw both assets by providing maximum amount of asset1, asset2's amount will be calculated accord...
static std::pair< TER, bool > deleteAMMAccountIfEmpty(Sandbox &sb, SLE::pointer const ammSle, STAmount const &lpTokenBalance, Asset const &asset1, Asset const &asset2, beast::Journal const &journal)
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > withdraw(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, AccountID const &account, STAmount const &amountBalance, STAmount const &amountWithdraw, std::optional< STAmount > const &amount2Withdraw, STAmount const &lpTokensAMMBalance, STAmount const &lpTokensWithdraw, std::uint16_t tfee, FreezeHandling freezeHandling, AuthHandling authHandling, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Withdraw requested assets and token from AMM into LP account.
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > equalWithdrawTokens(Sandbox &view, SLE const &ammSle, AccountID const account, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, STAmount const &lpTokensWithdraw, std::uint16_t tfee, FreezeHandling freezeHandling, AuthHandling authHandling, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Equal-asset withdrawal (LPTokens) of some AMM instance pools shares represented by the number of LPTo...
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 SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Asset const & asset() const
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFlag(std::uint32_t) const
Discardable, editable view to a ledger.
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
Rules const & rules() const override
Returns the tx processing rules.
Keylet mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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.
bool isXRP(AccountID const &c)
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::expected< bool, TER > verifyAndAdjustLPTokenBalance(Sandbox &sb, STAmount const &lpTokens, SLE::pointer &ammSle, AccountID const &account)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance.
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
std::string to_string(BaseUInt< Bits, Tag > const &a)
TERSubset< CanCvtToNotTEC > NotTEC
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
TER checkAMMPrecisionLoss(Number const &poolProductMean, STAmount const &newLPTokenBalance)
Check AMM pool product invariant after an AMM operation that changes LP tokens (deposit/withdraw/claw...
TER directSendNoFee(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static directSendNoFeeIOU if saAmount represents Issue.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
std::expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Asset > const &optAsset1, std::optional< Asset > const &optAsset2, FreezeHandling freezeHandling, AuthHandling authHandling, beast::Journal const j)
Get AMM pool and LP token balances.
STAmount toSTAmount(IOUAmount const &iou, Asset const &asset)
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.