rippled
Loading...
Searching...
No Matches
VaultClawback.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2025 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/tx/detail/VaultClawback.h>
21#include <xrpld/ledger/View.h>
22
23#include <xrpl/beast/utility/instrumentation.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/MPTIssue.h>
26#include <xrpl/protocol/STAmount.h>
27#include <xrpl/protocol/STNumber.h>
28#include <xrpl/protocol/TER.h>
29#include <xrpl/protocol/TxFlags.h>
30
31namespace ripple {
32
35{
36 if (!ctx.rules.enabled(featureSingleAssetVault))
37 return temDISABLED;
38
39 if (auto const ter = preflight1(ctx))
40 return ter;
41
42 if (ctx.tx.getFlags() & tfUniversalMask)
43 return temINVALID_FLAG;
44
45 if (ctx.tx[sfVaultID] == beast::zero)
46 {
47 JLOG(ctx.j.debug()) << "VaultClawback: zero/empty vault ID.";
48 return temMALFORMED;
49 }
50
51 AccountID const issuer = ctx.tx[sfAccount];
52 AccountID const holder = ctx.tx[sfHolder];
53
54 if (issuer == holder)
55 {
56 JLOG(ctx.j.debug()) << "VaultClawback: issuer cannot be holder.";
57 return temMALFORMED;
58 }
59
60 auto const amount = ctx.tx[~sfAmount];
61 if (amount)
62 {
63 // Note, zero amount is valid, it means "all". It is also the default.
64 if (*amount < beast::zero)
65 return temBAD_AMOUNT;
66 else if (isXRP(amount->asset()))
67 {
68 JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback XRP.";
69 return temMALFORMED;
70 }
71 else if (amount->asset().getIssuer() != issuer)
72 {
73 JLOG(ctx.j.debug())
74 << "VaultClawback: only asset issuer can clawback.";
75 return temMALFORMED;
76 }
77 }
78
79 return preflight2(ctx);
80}
81
82TER
84{
85 auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
86 if (!vault)
87 return tecNO_ENTRY;
88
89 auto account = ctx.tx[sfAccount];
90 auto const issuer = ctx.view.read(keylet::account(account));
91 if (!issuer)
92 {
93 // LCOV_EXCL_START
94 JLOG(ctx.j.error()) << "VaultClawback: missing issuer account.";
95 return tefINTERNAL;
96 // LCOV_EXCL_STOP
97 }
98
99 Asset const vaultAsset = vault->at(sfAsset);
100 if (auto const amount = ctx.tx[~sfAmount];
101 amount && vaultAsset != amount->asset())
102 return tecWRONG_ASSET;
103
104 if (vaultAsset.native())
105 {
106 JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback XRP.";
107 return tecNO_PERMISSION; // Cannot clawback XRP.
108 }
109 else if (vaultAsset.getIssuer() != account)
110 {
111 JLOG(ctx.j.debug()) << "VaultClawback: only asset issuer can clawback.";
112 return tecNO_PERMISSION; // Only issuers can clawback.
113 }
114
115 if (vaultAsset.holds<MPTIssue>())
116 {
117 auto const mpt = vaultAsset.get<MPTIssue>();
118 auto const mptIssue =
119 ctx.view.read(keylet::mptIssuance(mpt.getMptID()));
120 if (mptIssue == nullptr)
121 return tecOBJECT_NOT_FOUND;
122
123 std::uint32_t const issueFlags = mptIssue->getFieldU32(sfFlags);
124 if (!(issueFlags & lsfMPTCanClawback))
125 {
126 JLOG(ctx.j.debug())
127 << "VaultClawback: cannot clawback MPT vault asset.";
128 return tecNO_PERMISSION;
129 }
130 }
131 else if (vaultAsset.holds<Issue>())
132 {
133 std::uint32_t const issuerFlags = issuer->getFieldU32(sfFlags);
134 if (!(issuerFlags & lsfAllowTrustLineClawback) ||
135 (issuerFlags & lsfNoFreeze))
136 {
137 JLOG(ctx.j.debug())
138 << "VaultClawback: cannot clawback IOU vault asset.";
139 return tecNO_PERMISSION;
140 }
141 }
142
143 return tesSUCCESS;
144}
145
146TER
148{
149 auto const& tx = ctx_.tx;
150 auto const vault = view().peek(keylet::vault(tx[sfVaultID]));
151 if (!vault)
152 return tefINTERNAL; // LCOV_EXCL_LINE
153
154 auto const mptIssuanceID = (*vault)[sfShareMPTID];
155 auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID));
156 if (!sleIssuance)
157 {
158 // LCOV_EXCL_START
159 JLOG(j_.error()) << "VaultClawback: missing issuance of vault shares.";
160 return tefINTERNAL;
161 // LCOV_EXCL_STOP
162 }
163
164 Asset const asset = vault->at(sfAsset);
165 STAmount const amount = [&]() -> STAmount {
166 auto const maybeAmount = tx[~sfAmount];
167 if (maybeAmount)
168 return *maybeAmount;
169 return {sfAmount, asset, 0};
170 }();
171 XRPL_ASSERT(
172 amount.asset() == asset,
173 "ripple::VaultClawback::doApply : matching asset");
174
175 AccountID holder = tx[sfHolder];
176 STAmount assets, shares;
177 if (amount == beast::zero)
178 {
179 Asset share = *(*vault)[sfShareMPTID];
180 shares = accountHolds(
181 view(),
182 holder,
183 share,
186 j_);
187 assets = sharesToAssetsWithdraw(vault, sleIssuance, shares);
188 }
189 else
190 {
191 assets = amount;
192 shares = assetsToSharesWithdraw(vault, sleIssuance, assets);
193 }
194
195 // Clamp to maximum.
196 Number maxAssets = *vault->at(sfAssetsAvailable);
197 if (assets > maxAssets)
198 {
199 assets = maxAssets;
200 shares = assetsToSharesWithdraw(vault, sleIssuance, assets);
201 }
202
203 if (shares == beast::zero)
205
206 vault->at(sfAssetsTotal) -= assets;
207 vault->at(sfAssetsAvailable) -= assets;
208 view().update(vault);
209
210 auto const& vaultAccount = vault->at(sfAccount);
211 // Transfer shares from holder to vault.
212 if (auto ter = accountSend(
213 view(), holder, vaultAccount, shares, j_, WaiveTransferFee::Yes))
214 return ter;
215
216 // Transfer assets from vault to issuer.
217 if (auto ter = accountSend(
218 view(), vaultAccount, account_, assets, j_, WaiveTransferFee::Yes))
219 return ter;
220
221 // Sanity check
222 if (accountHolds(
223 view(),
224 vaultAccount,
225 assets.asset(),
228 j_) < beast::zero)
229 {
230 // LCOV_EXCL_START
231 JLOG(j_.error()) << "VaultClawback: negative balance of vault assets.";
232 return tefINTERNAL;
233 // LCOV_EXCL_STOP
234 }
235
236 return tesSUCCESS;
237}
238
239} // namespace ripple
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
bool native() const
Definition: Asset.h:101
constexpr TIss const & get() const
AccountID const & getIssuer() const
Definition: Asset.cpp:36
constexpr bool holds() const
Definition: Asset.h:132
A currency issued by an account.
Definition: Issue.h:36
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
Asset const & asset() const
Definition: STAmount.h:483
std::uint32_t getFlags() const
Definition: STObject.cpp:537
AccountID const account_
Definition: Transactor.h:93
ApplyView & view()
Definition: Transactor.h:109
beast::Journal const j_
Definition: Transactor.h:91
ApplyContext & ctx_
Definition: Transactor.h:90
static NotTEC preflight(PreflightContext const &ctx)
TER doApply() override
static TER preclaim(PreclaimContext const &ctx)
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:519
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition: Indexes.cpp:557
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
STAmount sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition: View.cpp:2677
@ lsfAllowTrustLineClawback
@ lsfMPTCanClawback
@ ahIGNORE_AUTH
Definition: View.h:81
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:83
@ tefINTERNAL
Definition: TER.h:173
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:144
@ tecNO_ENTRY
Definition: TER.h:306
@ tecOBJECT_NOT_FOUND
Definition: TER.h:326
@ tecINSUFFICIENT_FUNDS
Definition: TER.h:325
@ tecNO_PERMISSION
Definition: TER.h:305
@ tecWRONG_ASSET
Definition: TER.h:360
@ tesSUCCESS
Definition: TER.h:244
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:387
STAmount assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition: View.cpp:2658
constexpr std::uint32_t tfUniversalMask
Definition: TxFlags.h:62
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Calls static accountSendIOU if saAmount represents Issue.
Definition: View.cpp:1978
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:55
ReadView const & view
Definition: Transactor.h:58
beast::Journal const j
Definition: Transactor.h:62
State information when preflighting a tx.
Definition: Transactor.h:34
beast::Journal const j
Definition: Transactor.h:40