rippled
Loading...
Searching...
No Matches
AMMClawback.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2024 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/misc/AMMHelpers.h>
21#include <xrpld/app/misc/AMMUtils.h>
22#include <xrpld/app/tx/detail/AMMClawback.h>
23#include <xrpld/app/tx/detail/AMMWithdraw.h>
24#include <xrpld/ledger/Sandbox.h>
25#include <xrpld/ledger/View.h>
26
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/Indexes.h>
29#include <xrpl/protocol/TxFlags.h>
30#include <xrpl/protocol/st.h>
31
32#include <tuple>
33
34namespace ripple {
35
38{
39 if (!ctx.rules.enabled(featureAMMClawback))
40 return temDISABLED;
41
42 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
43 return ret; // LCOV_EXCL_LINE
44
45 auto const flags = ctx.tx.getFlags();
47 return temINVALID_FLAG;
48
49 AccountID const issuer = ctx.tx[sfAccount];
50 AccountID const holder = ctx.tx[sfHolder];
51
52 if (issuer == holder)
53 {
54 JLOG(ctx.j.trace())
55 << "AMMClawback: holder cannot be the same as issuer.";
56 return temMALFORMED;
57 }
58
59 std::optional<STAmount> const clawAmount = ctx.tx[~sfAmount];
60 auto const asset = ctx.tx[sfAsset].get<Issue>();
61 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
62
63 if (isXRP(asset))
64 return temMALFORMED;
65
66 if (flags & tfClawTwoAssets && asset.account != asset2.account)
67 {
68 JLOG(ctx.j.trace())
69 << "AMMClawback: tfClawTwoAssets can only be enabled when two "
70 "assets in the AMM pool are both issued by the issuer";
71 return temINVALID_FLAG;
72 }
73
74 if (asset.account != issuer)
75 {
76 JLOG(ctx.j.trace()) << "AMMClawback: Asset's account does not "
77 "match Account field.";
78 return temMALFORMED;
79 }
80
81 if (clawAmount && clawAmount->get<Issue>() != asset)
82 {
83 JLOG(ctx.j.trace()) << "AMMClawback: Amount's issuer/currency subfield "
84 "does not match Asset field";
85 return temBAD_AMOUNT;
86 }
87
88 if (clawAmount && *clawAmount <= beast::zero)
89 return temBAD_AMOUNT;
90
91 return preflight2(ctx);
92}
93
94TER
96{
97 auto const asset = ctx.tx[sfAsset].get<Issue>();
98 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
99 auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
100 if (!sleIssuer)
101 return terNO_ACCOUNT; // LCOV_EXCL_LINE
102
103 if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
104 return terNO_ACCOUNT;
105
106 auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
107 if (!ammSle)
108 {
109 JLOG(ctx.j.debug()) << "AMM Clawback: Invalid asset pair.";
110 return terNO_AMM;
111 }
112
113 std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags);
114
115 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
116 // permission
117 if (!(issuerFlagsIn & lsfAllowTrustLineClawback) ||
118 (issuerFlagsIn & lsfNoFreeze))
119 return tecNO_PERMISSION;
120
121 return tesSUCCESS;
122}
123
124TER
126{
127 Sandbox sb(&ctx_.view());
128
129 auto const ter = applyGuts(sb);
130 if (ter == tesSUCCESS)
131 sb.apply(ctx_.rawView());
132
133 return ter;
134}
135
136TER
138{
139 std::optional<STAmount> const clawAmount = ctx_.tx[~sfAmount];
140 AccountID const issuer = ctx_.tx[sfAccount];
141 AccountID const holder = ctx_.tx[sfHolder];
142 Issue const asset = ctx_.tx[sfAsset].get<Issue>();
143 Issue const asset2 = ctx_.tx[sfAsset2].get<Issue>();
144
145 auto ammSle = sb.peek(keylet::amm(asset, asset2));
146 if (!ammSle)
147 return tecINTERNAL; // LCOV_EXCL_LINE
148
149 auto const ammAccount = (*ammSle)[sfAccount];
150 auto const accountSle = sb.read(keylet::account(ammAccount));
151 if (!accountSle)
152 return tecINTERNAL; // LCOV_EXCL_LINE
153
154 auto const expected = ammHolds(
155 sb,
156 *ammSle,
157 asset,
158 asset2,
160 ctx_.journal);
161
162 if (!expected)
163 return expected.error(); // LCOV_EXCL_LINE
164 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
165
166 TER result;
167 STAmount newLPTokenBalance;
168 STAmount amountWithdraw;
169 std::optional<STAmount> amount2Withdraw;
170
171 auto const holdLPtokens = ammLPHolds(sb, *ammSle, holder, j_);
172 if (holdLPtokens == beast::zero)
173 return tecAMM_BALANCE;
174
175 if (!clawAmount)
176 // Because we are doing a two-asset withdrawal,
177 // tfee is actually not used, so pass tfee as 0.
178 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
180 sb,
181 *ammSle,
182 holder,
183 ammAccount,
184 amountBalance,
185 amount2Balance,
186 lptAMMBalance,
187 holdLPtokens,
188 holdLPtokens,
189 0,
193 ctx_.journal);
194 else
195 std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
197 sb,
198 *ammSle,
199 holder,
200 ammAccount,
201 amountBalance,
202 amount2Balance,
203 lptAMMBalance,
204 holdLPtokens,
205 *clawAmount);
206
207 if (result != tesSUCCESS)
208 return result; // LCOV_EXCL_LINE
209
211 sb, ammSle, newLPTokenBalance, asset, asset2, j_);
212 if (!res.second)
213 return res.first; // LCOV_EXCL_LINE
214
215 JLOG(ctx_.journal.trace())
216 << "AMM Withdraw during AMMClawback: lptoken new balance: "
217 << to_string(newLPTokenBalance.iou())
218 << " old balance: " << to_string(lptAMMBalance.iou());
219
220 auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_);
221 if (ter != tesSUCCESS)
222 return ter; // LCOV_EXCL_LINE
223
224 // if the issuer issues both assets and sets flag tfClawTwoAssets, we
225 // will claw the paired asset as well. We already checked if
226 // tfClawTwoAssets is enabled, the two assets have to be issued by the
227 // same issuer.
228 if (!amount2Withdraw)
229 return tecINTERNAL; // LCOV_EXCL_LINE
230
231 auto const flags = ctx_.tx.getFlags();
233 return rippleCredit(sb, holder, issuer, *amount2Withdraw, true, j_);
234
235 return tesSUCCESS;
236}
237
240 Sandbox& sb,
241 SLE const& ammSle,
242 AccountID const& holder,
243 AccountID const& ammAccount,
244 STAmount const& amountBalance,
245 STAmount const& amount2Balance,
246 STAmount const& lptAMMBalance,
247 STAmount const& holdLPtokens,
248 STAmount const& amount)
249{
250 auto frac = Number{amount} / amountBalance;
251 auto const amount2Withdraw = amount2Balance * frac;
252
253 auto const lpTokensWithdraw =
254 toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
255 if (lpTokensWithdraw > holdLPtokens)
256 // if lptoken balance less than what the issuer intended to clawback,
257 // clawback all the tokens. Because we are doing a two-asset withdrawal,
258 // tfee is actually not used, so pass tfee as 0.
260 sb,
261 ammSle,
262 holder,
263 ammAccount,
264 amountBalance,
265 amount2Balance,
266 lptAMMBalance,
267 holdLPtokens,
268 holdLPtokens,
269 0,
273 ctx_.journal);
274
275 // Because we are doing a two-asset withdrawal,
276 // tfee is actually not used, so pass tfee as 0.
278 sb,
279 ammSle,
280 ammAccount,
281 holder,
282 amountBalance,
283 amount,
284 toSTAmount(amount2Balance.issue(), amount2Withdraw),
285 lptAMMBalance,
286 toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
287 0,
291 ctx_.journal);
292}
293
294} // namespace ripple
Stream debug() const
Definition: Journal.h:328
Stream trace() const
Severity stream access functions.
Definition: Journal.h:322
TER doApply() override
TER applyGuts(Sandbox &view)
static NotTEC preflight(PreflightContext const &ctx)
Definition: AMMClawback.cpp:37
static TER preclaim(PreclaimContext const &ctx)
Definition: AMMClawback.cpp:95
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.
RawView & rawView()
Definition: ApplyContext.h:91
ApplyView & view()
Definition: ApplyContext.h:78
beast::Journal const journal
Definition: ApplyContext.h:75
A currency issued by an account.
Definition: Issue.h:36
AccountID account
Definition: Issue.h:39
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:130
IOUAmount iou() const
Definition: STAmount.cpp:322
Issue const & issue() const
Definition: STAmount.h:496
std::uint32_t getFlags() const
Definition: STObject.cpp:537
Discardable, editable view to a ledger.
Definition: Sandbox.h:35
void apply(RawView &to)
Definition: Sandbox.h:55
beast::Journal const j_
Definition: Transactor.h:141
XRPAmount mPriorBalance
Definition: Transactor.h:144
ApplyContext & ctx_
Definition: Transactor.h:140
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Match set account flags.
Definition: flags.h:125
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:439
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ fhIGNORE_FREEZE
Definition: View.h:78
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
@ lsfAllowTrustLineClawback
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
constexpr std::uint32_t tfClawTwoAssets
Definition: TxFlags.h:232
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:91
constexpr std::uint32_t tfAMMClawbackMask
Definition: TxFlags.h:233
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:112
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:160
@ tecINTERNAL
Definition: TER.h:310
@ tecNO_PERMISSION
Definition: TER.h:305
@ tecAMM_BALANCE
Definition: TER.h:329
@ tesSUCCESS
Definition: TER.h:244
bool isTesSuccess(TER x) noexcept
Definition: TER.h:672
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:46
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
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:2612
@ terNO_ACCOUNT
Definition: TER.h:217
@ terNO_AMM
Definition: TER.h:227
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:603
@ temBAD_AMOUNT
Definition: TER.h:89
@ temMALFORMED
Definition: TER.h:87
@ temINVALID_FLAG
Definition: TER.h:111
@ temDISABLED
Definition: TER.h:114
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:79
ReadView const & view
Definition: Transactor.h:82
beast::Journal const j
Definition: Transactor.h:87
State information when preflighting a tx.
Definition: Transactor.h:34
beast::Journal const j
Definition: Transactor.h:41
T tie(T... args)