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