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 xrpl {
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()) << "AMMClawback: holder cannot be the same as issuer.";
32 return temMALFORMED;
33 }
34
35 std::optional<STAmount> const clawAmount = ctx.tx[~sfAmount];
36 auto const asset = ctx.tx[sfAsset].get<Issue>();
37 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
38
39 if (isXRP(asset))
40 return temMALFORMED;
41
42 auto const flags = ctx.tx.getFlags();
43
44 if (flags & tfClawTwoAssets && asset.account != asset2.account)
45 {
46 JLOG(ctx.j.trace()) << "AMMClawback: tfClawTwoAssets can only be enabled when two "
47 "assets in the AMM pool are both issued by the issuer";
48 return temINVALID_FLAG;
49 }
50
51 if (asset.account != issuer)
52 {
53 JLOG(ctx.j.trace()) << "AMMClawback: Asset's account does not "
54 "match Account field.";
55 return temMALFORMED;
56 }
57
58 if (clawAmount && clawAmount->get<Issue>() != asset)
59 {
60 JLOG(ctx.j.trace()) << "AMMClawback: Amount's issuer/currency subfield "
61 "does not match Asset field";
62 return temBAD_AMOUNT;
63 }
64
65 if (clawAmount && *clawAmount <= beast::zero)
66 return temBAD_AMOUNT;
67
68 return tesSUCCESS;
69}
70
71TER
73{
74 auto const asset = ctx.tx[sfAsset].get<Issue>();
75 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
76 auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
77 if (!sleIssuer)
78 return terNO_ACCOUNT; // LCOV_EXCL_LINE
79
80 if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
81 return terNO_ACCOUNT;
82
83 auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
84 if (!ammSle)
85 {
86 JLOG(ctx.j.debug()) << "AMM Clawback: Invalid asset pair.";
87 return terNO_AMM;
88 }
89
90 std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags);
91
92 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
93 // permission
94 if (!(issuerFlagsIn & lsfAllowTrustLineClawback) || (issuerFlagsIn & lsfNoFreeze))
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 (ter == tesSUCCESS)
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
133 // inconsistent error behavior
134 auto const lpTokenBalance = ammLPHolds(sb, *ammSle, holder, j_);
135 if (lpTokenBalance == beast::zero)
136 return tecAMM_BALANCE;
137
138 if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokenBalance, ammSle, holder); !res)
139 return res.error(); // LCOV_EXCL_LINE
140 }
141
142 auto const expected = 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 auto const holdLPtokens = ammLPHolds(sb, *ammSle, holder, j_);
154 if (holdLPtokens == beast::zero)
155 return tecAMM_BALANCE;
156
157 if (!clawAmount)
158 // Because we are doing a two-asset withdrawal,
159 // tfee is actually not used, so pass tfee as 0.
160 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = AMMWithdraw::equalWithdrawTokens(
161 sb,
162 *ammSle,
163 holder,
164 ammAccount,
165 amountBalance,
166 amount2Balance,
167 lptAMMBalance,
168 holdLPtokens,
169 holdLPtokens,
170 0,
174 ctx_.journal);
175 else
176 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) = equalWithdrawMatchingOneAmount(
177 sb, *ammSle, holder, ammAccount, amountBalance, amount2Balance, lptAMMBalance, holdLPtokens, *clawAmount);
178
179 if (result != tesSUCCESS)
180 return result; // LCOV_EXCL_LINE
181
182 auto const res = AMMWithdraw::deleteAMMAccountIfEmpty(sb, ammSle, newLPTokenBalance, asset, asset2, j_);
183 if (!res.second)
184 return res.first; // LCOV_EXCL_LINE
185
186 JLOG(ctx_.journal.trace()) << "AMM Withdraw during AMMClawback: lptoken new balance: "
187 << to_string(newLPTokenBalance.iou())
188 << " old balance: " << to_string(lptAMMBalance.iou());
189
190 auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_);
191 if (ter != tesSUCCESS)
192 return ter; // LCOV_EXCL_LINE
193
194 // if the issuer issues both assets and sets flag tfClawTwoAssets, we
195 // will claw the paired asset as well. We already checked if
196 // tfClawTwoAssets is enabled, the two assets have to be issued by the
197 // same issuer.
198 if (!amount2Withdraw)
199 return tecINTERNAL; // LCOV_EXCL_LINE
200
201 auto const flags = ctx_.tx.getFlags();
202 if (flags & tfClawTwoAssets)
203 return rippleCredit(sb, holder, issuer, *amount2Withdraw, true, j_);
204
205 return tesSUCCESS;
206}
207
210 Sandbox& sb,
211 SLE const& ammSle,
212 AccountID const& holder,
213 AccountID const& ammAccount,
214 STAmount const& amountBalance,
215 STAmount const& amount2Balance,
216 STAmount const& lptAMMBalance,
217 STAmount const& holdLPtokens,
218 STAmount const& amount)
219{
220 auto frac = Number{amount} / amountBalance;
221 auto amount2Withdraw = amount2Balance * frac;
222
223 auto const lpTokensWithdraw = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
224
225 if (lpTokensWithdraw > holdLPtokens)
226 // if lptoken balance less than what the issuer intended to clawback,
227 // clawback all the tokens. Because we are doing a two-asset withdrawal,
228 // tfee is actually not used, so pass tfee as 0.
230 sb,
231 ammSle,
232 holder,
233 ammAccount,
234 amountBalance,
235 amount2Balance,
236 lptAMMBalance,
237 holdLPtokens,
238 holdLPtokens,
239 0,
243 ctx_.journal);
244
245 auto const& rules = sb.rules();
246 if (rules.enabled(fixAMMClawbackRounding))
247 {
248 auto tokensAdj = getRoundedLPTokens(rules, lptAMMBalance, frac, IsDeposit::No);
249
250 // LCOV_EXCL_START
251 if (tokensAdj == beast::zero)
253 // LCOV_EXCL_STOP
254
255 frac = adjustFracByTokens(rules, lptAMMBalance, tokensAdj, frac);
256 auto amount2Rounded = getRoundedAsset(rules, amount2Balance, frac, IsDeposit::No);
257
258 auto amountRounded = getRoundedAsset(rules, amountBalance, frac, IsDeposit::No);
259
261 sb,
262 ammSle,
263 ammAccount,
264 holder,
265 amountBalance,
266 amountRounded,
267 amount2Rounded,
268 lptAMMBalance,
269 tokensAdj,
270 0,
274 ctx_.journal);
275 }
276
277 // Because we are doing a two-asset withdrawal,
278 // tfee is actually not used, so pass tfee as 0.
280 sb,
281 ammSle,
282 ammAccount,
283 holder,
284 amountBalance,
285 amount,
286 toSTAmount(amount2Balance.issue(), amount2Withdraw),
287 lptAMMBalance,
288 toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
289 0,
293 ctx_.journal);
294}
295
296} // namespace xrpl
Stream debug() const
Definition Journal.h:300
Stream trace() const
Severity stream access functions.
Definition Journal.h:294
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:118
Issue const & issue() const
Definition STAmount.h:454
IOUAmount iou() const
Definition STAmount.cpp:264
std::uint32_t getFlags() const
Definition STObject.cpp:492
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:110
XRPAmount mPriorBalance
Definition Transactor.h:113
ApplyContext & ctx_
Definition Transactor.h:108
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:393
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
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
Definition View.h:58
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:597
constexpr std::uint32_t tfAMMClawbackMask
Definition TxFlags.h:243
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:596
@ temINVALID_FLAG
Definition TER.h:91
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
constexpr std::uint32_t tfClawTwoAssets
Definition TxFlags.h:242
@ tecAMM_INVALID_TOKENS
Definition TER.h:312
@ tecINTERNAL
Definition TER.h:291
@ tecAMM_BALANCE
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:286
@ lsfAllowTrustLineClawback
@ lsfNoFreeze
@ 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.
Definition View.cpp:3083
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:80
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:390
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
beast::Journal const j
Definition Transactor.h:61
State information when preflighting a tx.
Definition Transactor.h:15
beast::Journal const j
Definition Transactor.h:22
T tie(T... args)