rippled
Loading...
Searching...
No Matches
AMMClawback.cpp
1#include <xrpl/ledger/Sandbox.h>
2#include <xrpl/ledger/View.h>
3#include <xrpl/protocol/Feature.h>
4#include <xrpl/protocol/Indexes.h>
5#include <xrpl/protocol/TxFlags.h>
6#include <xrpl/protocol/st.h>
7#include <xrpl/tx/transactors/dex/AMMClawback.h>
8#include <xrpl/tx/transactors/dex/AMMHelpers.h>
9#include <xrpl/tx/transactors/dex/AMMUtils.h>
10#include <xrpl/tx/transactors/dex/AMMWithdraw.h>
11
12#include <tuple>
13
14namespace xrpl {
15
18{
19 return tfAMMClawbackMask;
20}
21
24{
25 AccountID const issuer = ctx.tx[sfAccount];
26 AccountID const holder = ctx.tx[sfHolder];
27
28 if (issuer == holder)
29 {
30 JLOG(ctx.j.trace()) << "AMMClawback: holder cannot be the same as issuer.";
31 return temMALFORMED;
32 }
33
34 std::optional<STAmount> const clawAmount = ctx.tx[~sfAmount];
35 auto const asset = ctx.tx[sfAsset].get<Issue>();
36 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
37
38 if (isXRP(asset))
39 return temMALFORMED;
40
41 auto const flags = ctx.tx.getFlags();
42
43 if (((flags & tfClawTwoAssets) != 0u) && asset.account != asset2.account)
44 {
45 JLOG(ctx.j.trace()) << "AMMClawback: tfClawTwoAssets can only be enabled when two "
46 "assets in the AMM pool are both issued by the issuer";
47 return temINVALID_FLAG;
48 }
49
50 if (asset.account != issuer)
51 {
52 JLOG(ctx.j.trace()) << "AMMClawback: Asset's account does not "
53 "match Account field.";
54 return temMALFORMED;
55 }
56
57 if (clawAmount && clawAmount->get<Issue>() != asset)
58 {
59 JLOG(ctx.j.trace()) << "AMMClawback: Amount's issuer/currency subfield "
60 "does not match Asset field";
61 return temBAD_AMOUNT;
62 }
63
64 if (clawAmount && *clawAmount <= beast::zero)
65 return temBAD_AMOUNT;
66
67 return tesSUCCESS;
68}
69
70TER
72{
73 auto const asset = ctx.tx[sfAsset].get<Issue>();
74 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
75 auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
76 if (!sleIssuer)
77 return terNO_ACCOUNT; // LCOV_EXCL_LINE
78
79 if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
80 return terNO_ACCOUNT;
81
82 auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
83 if (!ammSle)
84 {
85 JLOG(ctx.j.debug()) << "AMM Clawback: Invalid asset pair.";
86 return terNO_AMM;
87 }
88
89 std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags);
90
91 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
92 // permission
93 if (((issuerFlagsIn & lsfAllowTrustLineClawback) == 0u) ||
94 ((issuerFlagsIn & lsfNoFreeze) != 0u))
95 return tecNO_PERMISSION;
96
97 return tesSUCCESS;
98}
99
100TER
102{
103 Sandbox sb(&ctx_.view());
104
105 auto const ter = applyGuts(sb);
106 if (isTesSuccess(ter))
107 sb.apply(ctx_.rawView());
108
109 return ter;
110}
111
112TER
114{
115 std::optional<STAmount> const clawAmount = ctx_.tx[~sfAmount];
116 AccountID const issuer = ctx_.tx[sfAccount];
117 AccountID const holder = ctx_.tx[sfHolder];
118 Issue const asset = ctx_.tx[sfAsset].get<Issue>();
119 Issue const asset2 = ctx_.tx[sfAsset2].get<Issue>();
120
121 auto ammSle = sb.peek(keylet::amm(asset, asset2));
122 if (!ammSle)
123 return tecINTERNAL; // LCOV_EXCL_LINE
124
125 auto const ammAccount = (*ammSle)[sfAccount];
126 auto const accountSle = sb.read(keylet::account(ammAccount));
127 if (!accountSle)
128 return tecINTERNAL; // LCOV_EXCL_LINE
129
130 if (sb.rules().enabled(fixAMMClawbackRounding))
131 {
132 // retrieve LP token balance inside the amendment gate to avoid inconsistent error behavior
133 auto const lpTokenBalance = ammLPHolds(sb, *ammSle, holder, j_);
134 if (lpTokenBalance == beast::zero)
135 return tecAMM_BALANCE;
136
137 if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokenBalance, ammSle, holder);
138 !res)
139 return res.error(); // LCOV_EXCL_LINE
140 }
141 auto const expected =
142 ammHolds(sb, *ammSle, asset, asset2, FreezeHandling::fhIGNORE_FREEZE, ctx_.journal);
143
144 if (!expected)
145 return expected.error(); // LCOV_EXCL_LINE
146 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
147
148 TER result;
149 STAmount newLPTokenBalance;
150 STAmount amountWithdraw;
151 std::optional<STAmount> amount2Withdraw;
152
153 // calling a second time on purpose since `verifyAndAdjustLPTokenBalance` rounds and may adjust
154 // the balance
155 auto const holdLPtokens = ammLPHolds(sb, *ammSle, holder, j_);
156 if (holdLPtokens == beast::zero)
157 return tecAMM_BALANCE;
158
159 if (!clawAmount)
160 {
161 // Because we are doing a two-asset withdrawal,
162 // tfee is actually not used, so pass tfee as 0.
163 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
165 sb,
166 *ammSle,
167 holder,
168 ammAccount,
169 amountBalance,
170 amount2Balance,
171 lptAMMBalance,
172 holdLPtokens,
173 holdLPtokens,
174 0,
178 ctx_.journal);
179 }
180 else
181 {
182 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
184 sb,
185 *ammSle,
186 holder,
187 ammAccount,
188 amountBalance,
189 amount2Balance,
190 lptAMMBalance,
191 holdLPtokens,
192 *clawAmount);
193 }
194
195 if (!isTesSuccess(result))
196 return result; // LCOV_EXCL_LINE
197
198 auto const res =
199 AMMWithdraw::deleteAMMAccountIfEmpty(sb, ammSle, newLPTokenBalance, asset, asset2, j_);
200 if (!res.second)
201 return res.first; // LCOV_EXCL_LINE
202
203 JLOG(ctx_.journal.trace()) << "AMM Withdraw during AMMClawback: lptoken new balance: "
204 << to_string(newLPTokenBalance.iou())
205 << " old balance: " << to_string(lptAMMBalance.iou());
206
207 auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_);
208 if (!isTesSuccess(ter))
209 return ter; // LCOV_EXCL_LINE
210
211 // if the issuer issues both assets and sets flag tfClawTwoAssets, we
212 // will claw the paired asset as well. We already checked if
213 // tfClawTwoAssets is enabled, the two assets have to be issued by the
214 // same issuer.
215 if (!amount2Withdraw)
216 return tecINTERNAL; // LCOV_EXCL_LINE
217
218 auto const flags = ctx_.tx.getFlags();
219 if ((flags & tfClawTwoAssets) != 0u)
220 return rippleCredit(sb, holder, issuer, *amount2Withdraw, true, j_);
221
222 return tesSUCCESS;
223}
224
227 Sandbox& sb,
228 SLE const& ammSle,
229 AccountID const& holder,
230 AccountID const& ammAccount,
231 STAmount const& amountBalance,
232 STAmount const& amount2Balance,
233 STAmount const& lptAMMBalance,
234 STAmount const& holdLPtokens,
235 STAmount const& amount)
236{
237 auto frac = Number{amount} / amountBalance;
238 auto amount2Withdraw = amount2Balance * frac;
239
240 auto const lpTokensWithdraw = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
241
242 if (lpTokensWithdraw > holdLPtokens)
243 {
244 // if lptoken balance less than what the issuer intended to clawback,
245 // clawback all the tokens. Because we are doing a two-asset withdrawal,
246 // tfee is actually not used, so pass tfee as 0.
248 sb,
249 ammSle,
250 holder,
251 ammAccount,
252 amountBalance,
253 amount2Balance,
254 lptAMMBalance,
255 holdLPtokens,
256 holdLPtokens,
257 0,
261 ctx_.journal);
262 }
263
264 auto const& rules = sb.rules();
265 if (rules.enabled(fixAMMClawbackRounding))
266 {
267 auto tokensAdj = getRoundedLPTokens(rules, lptAMMBalance, frac, IsDeposit::No);
268
269 // LCOV_EXCL_START
270 if (tokensAdj == beast::zero)
272 // LCOV_EXCL_STOP
273
274 frac = adjustFracByTokens(rules, lptAMMBalance, tokensAdj, frac);
275 auto amount2Rounded = getRoundedAsset(rules, amount2Balance, frac, IsDeposit::No);
276
277 auto amountRounded = getRoundedAsset(rules, amountBalance, frac, IsDeposit::No);
278
280 sb,
281 ammSle,
282 ammAccount,
283 holder,
284 amountBalance,
285 amountRounded,
286 amount2Rounded,
287 lptAMMBalance,
288 tokensAdj,
289 0,
293 ctx_.journal);
294 }
295
296 // Because we are doing a two-asset withdrawal,
297 // tfee is actually not used, so pass tfee as 0.
299 sb,
300 ammSle,
301 ammAccount,
302 holder,
303 amountBalance,
304 amount,
305 toSTAmount(amount2Balance.issue(), amount2Withdraw),
306 lptAMMBalance,
307 toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
308 0,
312 ctx_.journal);
313}
314
315} // namespace xrpl
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static std::uint32_t getFlagsMask(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...
TER doApply() override
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 freezeHanding, 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...
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, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Withdraw requested assets and token from AMM into LP account.
static std::pair< TER, bool > deleteAMMAccountIfEmpty(Sandbox &sb, std::shared_ptr< SLE > const ammSle, STAmount const &lpTokenBalance, Issue const &issue1, Issue const &issue2, beast::Journal const &journal)
STTx const & tx
beast::Journal const journal
RawView & rawView()
ApplyView & view()
A currency issued by an account.
Definition Issue.h:13
AccountID account
Definition Issue.h:16
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
virtual std::shared_ptr< SLE const > 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.
Definition Rules.cpp:120
Issue const & issue() const
Definition STAmount.h:470
IOUAmount iou() const
Definition STAmount.cpp:276
std::uint32_t getFlags() const
Definition STObject.cpp:509
Discardable, editable view to a ledger.
Definition Sandbox.h:15
void apply(RawView &to)
Definition Sandbox.h:35
beast::Journal const j_
Definition Transactor.h:114
XRPAmount preFeeBalance_
Definition Transactor.h:117
ApplyContext & ctx_
Definition Transactor.h:112
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Rules const & rules() const override
Returns the tx processing rules.
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
T is_same_v
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:404
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_AMM
Definition TER.h:207
@ terNO_ACCOUNT
Definition TER.h:197
@ fhIGNORE_FREEZE
bool isXRP(AccountID const &c)
Definition AccountID.h:70
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)
Definition base_uint.h:602
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
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.
Definition AMMUtils.cpp:26
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:614
@ temINVALID_FLAG
Definition TER.h:91
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
@ tecAMM_INVALID_TOKENS
Definition TER.h:312
@ tecINTERNAL
Definition TER.h:291
@ tecAMM_BALANCE
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:286
@ tesSUCCESS
Definition TER.h:225
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
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.
Definition AMMUtils.cpp:87
Expected< bool, TER > verifyAndAdjustLPTokenBalance(Sandbox &sb, STAmount const &lpTokens, std::shared_ptr< SLE > &ammSle, AccountID const &account)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance.
Definition AMMUtils.cpp:434
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:57
ReadView const & view
Definition Transactor.h:60
beast::Journal const j
Definition Transactor.h:65
State information when preflighting a tx.
Definition Transactor.h:14
beast::Journal const j
Definition Transactor.h:21
T tie(T... args)