xrpld
Loading...
Searching...
No Matches
VaultClawback.cpp
1#include <xrpl/tx/transactors/vault/VaultClawback.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/beast/utility/Zero.h>
7#include <xrpl/beast/utility/instrumentation.h>
8#include <xrpl/core/ServiceRegistry.h>
9#include <xrpl/ledger/helpers/AccountRootHelpers.h>
10#include <xrpl/ledger/helpers/TokenHelpers.h>
11#include <xrpl/ledger/helpers/VaultHelpers.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Asset.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/Indexes.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/LedgerFormats.h>
18#include <xrpl/protocol/MPTIssue.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/STLedgerEntry.h>
22#include <xrpl/protocol/STNumber.h> // IWYU pragma: keep
23#include <xrpl/protocol/STTakesAsset.h>
24#include <xrpl/protocol/STTx.h>
25#include <xrpl/protocol/TER.h>
26#include <xrpl/protocol/XRPAmount.h>
27#include <xrpl/tx/Transactor.h>
28
29#include <expected>
30#include <optional>
31#include <stdexcept>
32#include <utility>
33
34namespace xrpl {
37{
38 if (ctx.tx[sfVaultID] == beast::kZero)
39 {
40 JLOG(ctx.j.debug()) << "VaultClawback: zero/empty vault ID.";
41 return temMALFORMED;
42 }
43
44 auto const amount = ctx.tx[~sfAmount];
45 if (amount)
46 {
47 // Note, zero amount is valid, it means "all". It is also the default.
48 if (*amount < beast::kZero)
49 {
50 return temBAD_AMOUNT;
51 }
52 if (isXRP(amount->asset()))
53 {
54 JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback XRP.";
55 return temMALFORMED;
56 }
57 }
58
59 return tesSUCCESS;
60}
61
62[[nodiscard]] STAmount
64 SLE::ConstRef vault,
65 std::optional<STAmount> const& maybeAmount,
66 AccountID const& account)
67{
68 if (maybeAmount)
69 return *maybeAmount;
70
71 Asset const share = MPTIssue{vault->at(sfShareMPTID)};
72 if (account == vault->at(sfOwner))
73 return STAmount{share};
74
75 return STAmount{vault->at(sfAsset)};
76}
77
78TER
80{
81 auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
82 if (!vault)
83 return tecNO_ENTRY;
84
85 Asset const vaultAsset = vault->at(sfAsset);
86 auto const account = ctx.tx[sfAccount];
87 auto const holder = ctx.tx[sfHolder];
88 auto const maybeAmount = ctx.tx[~sfAmount];
89 auto const mptIssuanceID = vault->at(sfShareMPTID);
90 auto const sleShareIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
91 if (!sleShareIssuance)
92 {
93 // LCOV_EXCL_START
94 JLOG(ctx.j.error()) << "VaultClawback: missing issuance of vault shares.";
95 return tefINTERNAL;
96 // LCOV_EXCL_STOP
97 }
98
99 // A pseudo-account holds no vault shares, so a clawback naming one is a no-op: the vault's own
100 // pseudo-account issues the shares, and no flow hands them to another one.
101 // Pre-fixCleanup3_4_0: an implicit amount ends in tecPRECISION_LOSS, an explicit one debits the
102 // vault and trips the "shares must move" invariant.
103 // Post-fixCleanup3_4_0: refused here.
104 if (ctx.view.rules().enabled(fixCleanup3_4_0) && isPseudoAccount(ctx.view, holder))
105 {
106 JLOG(ctx.j.debug()) << "VaultClawback: holder is a pseudo-account.";
107 return tecPSEUDO_ACCOUNT;
108 }
109
110 Asset const share = MPTIssue{mptIssuanceID};
111
112 // Ambiguous case: If Issuer is Owner they must specify the asset
113 if (!maybeAmount && !vaultAsset.native() && vaultAsset.getIssuer() == vault->at(sfOwner))
114 {
115 JLOG(ctx.j.debug()) << "VaultClawback: must specify amount when issuer is owner.";
116 return tecWRONG_ASSET;
117 }
118
119 auto const amount = clawbackAmount(vault, maybeAmount, account);
120
121 // There is a special case that allows the VaultOwner to use clawback to
122 // burn shares when Vault assets total and available are zero, but
123 // shares remain. However, that case is handled in doApply() directly,
124 // so here we just enforce checks.
125 if (amount.asset() == share)
126 {
127 // Only the Vault Owner may clawback shares
128 if (account != vault->at(sfOwner))
129 {
130 JLOG(ctx.j.debug()) << "VaultClawback: only vault owner can clawback shares.";
131 return tecNO_PERMISSION;
132 }
133
134 auto const assetsTotal = vault->at(sfAssetsTotal);
135 auto const assetsAvailable = vault->at(sfAssetsAvailable);
136 auto const sharesTotal = sleShareIssuance->at(sfOutstandingAmount);
137
138 // Owner can clawback funds when the vault has shares but no assets
139 if (sharesTotal == 0 || (assetsTotal != 0 || assetsAvailable != 0))
140 {
141 JLOG(ctx.j.debug()) << "VaultClawback: vault owner can clawback shares only"
142 " when vault has no assets.";
143 return tecNO_PERMISSION;
144 }
145
146 // If amount is non-zero, the VaultOwner must burn all shares
147 if (amount != beast::kZero)
148 {
149 Number const& sharesHeld = accountHolds(
150 ctx.view,
151 holder,
152 share,
155 ctx.j);
156
157 // The VaultOwner must burn all shares
158 if (amount != sharesHeld)
159 {
160 JLOG(ctx.j.debug()) << "VaultClawback: vault owner must clawback all "
161 "shares.";
162 return tecLIMIT_EXCEEDED;
163 }
164 }
165
166 return tesSUCCESS;
167 }
168
169 // The asset that is being clawed back is the vault asset
170 if (amount.asset() == vaultAsset)
171 {
172 // XRP cannot be clawed back
173 if (vaultAsset.native())
174 {
175 JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback XRP.";
176 return tecNO_PERMISSION;
177 }
178
179 // Only the Asset Issuer may clawback the asset
180 if (account != vaultAsset.getIssuer())
181 {
182 JLOG(ctx.j.debug()) << "VaultClawback: only asset issuer can clawback asset.";
183 return tecNO_PERMISSION;
184 }
185
186 // The issuer cannot clawback from itself
187 if (account == holder)
188 {
189 JLOG(ctx.j.debug()) << "VaultClawback: issuer cannot be the holder.";
190 return tecNO_PERMISSION;
191 }
192
193 return vaultAsset.visit(
194 [&](MPTIssue const& issue) -> TER {
195 auto const mptIssue = ctx.view.read(keylet::mptokenIssuance(issue.getMptID()));
196 if (mptIssue == nullptr)
197 return tecOBJECT_NOT_FOUND;
198
199 if (!mptIssue->isFlag(lsfMPTCanClawback))
200 {
201 JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback "
202 "MPT vault asset.";
203 return tecNO_PERMISSION;
204 }
205
206 return tesSUCCESS;
207 },
208 [&](Issue const&) -> TER {
209 auto const issuerSle = ctx.view.read(keylet::account(account));
210 if (!issuerSle)
211 {
212 // LCOV_EXCL_START
213 JLOG(ctx.j.error()) << "VaultClawback: missing submitter account.";
214 return tefINTERNAL;
215 // LCOV_EXCL_STOP
216 }
217
218 if (!issuerSle->isFlag(lsfAllowTrustLineClawback) || issuerSle->isFlag(lsfNoFreeze))
219 {
220 JLOG(ctx.j.debug()) << "VaultClawback: cannot clawback "
221 "IOU vault asset.";
222 return tecNO_PERMISSION;
223 }
224
225 return tesSUCCESS;
226 });
227 }
228
229 // Invalid asset
230 return tecWRONG_ASSET;
231}
232
233std::expected<std::pair<STAmount, STAmount>, TER>
235 SLE::Ref vault,
236 SLE::ConstRef sleShareIssuance,
237 AccountID const& holder,
239{
240 bool const fix340Enabled = ctx_.view().rules().enabled(fixCleanup3_4_0);
241 if (clawbackAmount.asset() != vault->at(sfAsset))
242 {
243 // preclaim should have blocked this , now it's an internal error
244 // LCOV_EXCL_START
245 JLOG(j_.error()) << "VaultClawback: asset mismatch in clawback.";
247 // LCOV_EXCL_STOP
248 }
249
250 auto const assetsAvailable = vault->at(sfAssetsAvailable);
251 auto const mptIssuanceID = *vault->at(sfShareMPTID);
252 MPTIssue const share{mptIssuanceID};
253
254 // Pre-fixCleanup3_1_3: zero-amount clawback returned early without
255 // clamping to assetsAvailable, allowing more assets to be recovered
256 // than available when there was an outstanding loan. Retained for
257 // ledger replay compatibility.
258 if (!ctx_.view().rules().enabled(fixCleanup3_1_3) && clawbackAmount == beast::kZero)
259 {
260 auto const sharesDestroyed = accountHolds(
262 auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed);
263 if (!maybeAssets)
264 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
265
266 return std::make_pair(*maybeAssets, sharesDestroyed);
267 }
268
269 STAmount sharesDestroyed;
270 STAmount assetsRecovered;
271
272 // Number arithmetic can throw overflow_error when Scale and totals are large. Caught below.
273 try
274 {
275 // Do not discount a sole holder's shares: clawing back AssetsAvailable
276 // at the discounted rate can burn every share while loan assets remain.
277 auto const waiveUnrealizedLoss =
278 fix340Enabled && isSoleShareholder(view(), holder, sleShareIssuance)
281
283 {
284 // Zero amount means clawback all shares the holder has; derive the corresponding asset
285 // amount from the share balance.
286 // isSoleShareholder already established that the holder owns the
287 // entire outstanding share supply whenever the waiver applies, so
288 // sfOutstandingAmount gives sharesDestroyed directly, avoiding a
289 // redundant MPToken read via accountHolds.
290 sharesDestroyed = waiveUnrealizedLoss == WaiveUnrealizedLoss::Yes
291 ? STAmount{share, sleShareIssuance->at(sfOutstandingAmount)}
292 : accountHolds(
293 view(),
294 holder,
295 share,
298 j_);
299 auto const maybeAssets = sharesToAssetsWithdraw(
300 vault, sleShareIssuance, sharesDestroyed, waiveUnrealizedLoss);
301 if (!maybeAssets)
302 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
303
304 assetsRecovered = *maybeAssets;
305 }
306 else
307 {
308 // Pre-fixCleanup3_4_0: shares were rounded to nearest, so the
309 // round-trip back to assets could exceed clawbackAmount.
310 // Post-amendment: truncate shares so assetsRecovered <=
311 // clawbackAmount by construction (matches the clamp branch
312 // below).
313 auto const truncate = fix340Enabled ? TruncateShares::Yes : TruncateShares::No;
314 auto const maybeShares = assetsToSharesWithdraw(
315 vault, sleShareIssuance, clawbackAmount, truncate, waiveUnrealizedLoss);
316 if (!maybeShares)
317 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
318 sharesDestroyed = *maybeShares;
319
320 auto const maybeAssets = sharesToAssetsWithdraw(
321 vault, sleShareIssuance, sharesDestroyed, waiveUnrealizedLoss);
322 if (!maybeAssets)
323 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
324 assetsRecovered = *maybeAssets;
325 }
326 // Clamp assetsRecovered to sfAssetsAvailable, then re-derive shares and assets so the pair
327 // stays consistent.
328 if (assetsRecovered > *assetsAvailable)
329 {
330 assetsRecovered = *assetsAvailable;
331 {
332 auto const maybeShares = assetsToSharesWithdraw(
333 vault,
334 sleShareIssuance,
335 assetsRecovered,
337 waiveUnrealizedLoss);
338 if (!maybeShares)
339 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
340 sharesDestroyed = *maybeShares;
341 }
342
343 auto const maybeAssets = sharesToAssetsWithdraw(
344 vault, sleShareIssuance, sharesDestroyed, waiveUnrealizedLoss);
345 if (!maybeAssets)
346 return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
347 assetsRecovered = *maybeAssets;
348 // Truncation should guarantee the invariant holds. If it does not, a conversion
349 // helper is broken; refuse rather than over-recover.
350 if (assetsRecovered > *assetsAvailable)
351 {
352 // LCOV_EXCL_START
353 JLOG(j_.error()) << "VaultClawback: invalid rounding of shares.";
355 // LCOV_EXCL_STOP
356 }
357 }
358
359 // Post-fixCleanup3_4_0: round the recovery down at the posterior sfAssetsTotal scale so all
360 // rails change by the same representable delta. sharesDestroyed is intentionally NOT
361 // re-derived here: the holder's shares are burned for their pre-clamp value, so any
362 // sub-ULP trimmed off stays in the vault for the remaining shareholders.
363 if (ctx_.view().rules().enabled(fixCleanup3_4_0) && assetsRecovered > beast::kZero)
364 {
365 auto const maybeClamped = clampToAssetsTotalScale(vault, -assetsRecovered);
366 if (!maybeClamped)
367 return std::unexpected(maybeClamped.error());
368 assetsRecovered = *maybeClamped;
369 }
370 }
371 catch (std::overflow_error const&)
372 {
373 // It's easy to hit this exception from Number with large enough
374 // Scale so we avoid spamming the log and only use debug here.
375 JLOG(j_.debug()) //
376 << "VaultClawback: overflow error with"
377 << " scale=" << (int)vault->at(sfScale).value() //
378 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
379 << ", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount)
380 << ", amount=" << clawbackAmount.value();
381 // Overflow means this transaction cannot apply, but ledger state is still consistent.
382 // Return tecPATH_DRY rather than a hard internal error.
384 }
385
386 return std::make_pair(assetsRecovered, sharesDestroyed);
387}
388
389TER
391{
392 auto const& tx = ctx_.tx;
393 auto const vault = view().peek(keylet::vault(tx[sfVaultID]));
394 if (!vault)
395 return tefINTERNAL; // LCOV_EXCL_LINE
396
397 auto const mptIssuanceID = *vault->at(sfShareMPTID);
398 auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
399 if (!sleIssuance)
400 {
401 // LCOV_EXCL_START
402 JLOG(j_.error()) << "VaultClawback: missing issuance of vault shares.";
403 return tefINTERNAL;
404 // LCOV_EXCL_STOP
405 }
406 MPTIssue const share{mptIssuanceID};
407
408 Asset const vaultAsset = vault->at(sfAsset);
409 STAmount const amount = clawbackAmount(vault, tx[~sfAmount], accountID_);
410
411 auto assetsAvailable = vault->at(sfAssetsAvailable);
412 auto assetsTotal = vault->at(sfAssetsTotal);
413
414 AccountID const holder = tx[sfHolder];
415 STAmount sharesDestroyed = {share};
416 STAmount assetsRecovered = {vault->at(sfAsset)};
417
418 // The Owner is burning shares
419 if (accountID_ == vault->at(sfOwner) && amount.asset() == share)
420 {
421 sharesDestroyed = accountHolds(
423 }
424 else // The Issuer is clawbacking vault assets
425 {
426 XRPL_ASSERT(amount.asset() == vaultAsset, "xrpl::VaultClawback::doApply : matching asset");
427
428 auto const clawbackParts = assetsToClawback(vault, sleIssuance, holder, amount);
429 if (!clawbackParts)
430 return clawbackParts.error();
431
432 assetsRecovered = clawbackParts->first;
433 sharesDestroyed = clawbackParts->second;
434 }
435
436 // The holder has no shares (or the recovery clamped to zero). Nothing to burn; refuse rather
437 // than modifying vault state.
438 if (sharesDestroyed == beast::kZero)
439 return tecPRECISION_LOSS;
440
441 // Number arithmetic can throw overflow_error when Scale and totals are large.
442 if (view().rules().enabled(fixCleanup3_4_0))
443 {
444 try
445 {
446 // A non-zero recovery can be too small to change the stored sfAssetsTotal at
447 // STAmount's precision. Shares would still be burned, reject it instead.
448 if (debitIsNonZeroDust(vaultAsset, assetsTotal, assetsRecovered))
449 {
450 // LCOV_EXCL_START
451 JLOG(j_.debug())
452 << "VaultClawback: clawback amount too small to change stored vault"
453 " balance";
454 return tecPRECISION_LOSS;
455 // LCOV_EXCL_STOP
456 }
457 }
458 // LCOV_EXCL_START
459 catch (std::overflow_error const&)
460 {
461 // It's easy to hit this exception from Number with large enough Scale
462 // so we avoid spamming the log and only use debug here.
463 JLOG(j_.debug()) //
464 << "VaultClawback: overflow error with"
465 << " scale=" << (int)vault->at(sfScale).value() //
466 << ", assetsTotal=" << vault->at(sfAssetsTotal).value()
467 << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
468 << ", amount=" << amount.value();
469 // Overflow means this transaction cannot apply, but ledger state is still
470 // consistent. Return tecPATH_DRY rather than a hard internal error.
471 return tecPATH_DRY;
472 }
473 // LCOV_EXCL_STOP
474 }
475
476 assetsTotal -= assetsRecovered;
477 assetsAvailable -= assetsRecovered;
478 view().update(vault);
479
480 auto const& vaultAccount = vault->at(sfAccount);
481 // Transfer shares from holder to vault.
482 if (auto const ter = accountSend(
483 view(), holder, vaultAccount, sharesDestroyed, j_, {}, WaiveTransferFee::Yes);
484 !isTesSuccess(ter))
485 return ter;
486
487 // Try to remove MPToken for shares, if the holder balance is zero. Vault
488 // pseudo-account will never set lsfMPTAuthorized, so we ignore flags.
489 // Keep MPToken if holder is the vault owner.
490 if (holder != vault->at(sfOwner))
491 {
492 if (auto const ter =
493 removeEmptyHolding(ctx_.getApplyViewContext(), holder, sharesDestroyed.asset(), j_);
494 isTesSuccess(ter))
495 {
496 JLOG(j_.debug()) //
497 << "VaultClawback: removed empty MPToken for vault shares"
498 << " MPTID=" << to_string(mptIssuanceID) //
499 << " account=" << toBase58(holder);
500 }
501 else if (ter != tecHAS_OBLIGATIONS)
502 {
503 // LCOV_EXCL_START
504 JLOG(j_.error()) //
505 << "VaultClawback: failed to remove MPToken for vault shares"
506 << " MPTID=" << to_string(mptIssuanceID) //
507 << " account=" << toBase58(holder) //
508 << " with result: " << transToken(ter);
509 return ter;
510 // LCOV_EXCL_STOP
511 }
512 // else quietly ignore, holder balance is not zero
513 }
514
515 if (assetsRecovered > beast::kZero)
516 {
517 // Transfer assets from vault to issuer.
518 if (auto const ter = accountSend(
519 view(), vaultAccount, accountID_, assetsRecovered, j_, {}, WaiveTransferFee::Yes);
520 !isTesSuccess(ter))
521 return ter;
522
523 // Sanity check
524 if (accountHolds(
525 view(),
526 vaultAccount,
527 assetsRecovered.asset(),
530 j_) < beast::kZero)
531 {
532 // LCOV_EXCL_START
533 JLOG(j_.error()) << "VaultClawback: negative balance of vault assets.";
534 return tefINTERNAL;
535 // LCOV_EXCL_STOP
536 }
537 }
538
539 associateAsset(*vault, vaultAsset);
540
541 return tesSUCCESS;
542}
543
544void
546{
547 // No transaction-specific invariants yet (future work).
548}
549
550bool
552 STTx const&,
553 TER,
554 XRPAmount,
555 ReadView const&,
556 beast::Journal const&)
557{
558 // No transaction-specific invariants yet (future work).
559 return true;
560}
561
562} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream error() const
Definition Journal.h:362
Stream debug() const
Definition Journal.h:344
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void update(SLE::Ref sle)=0
Indicate changes to a peeked SLE.
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition Asset.h:117
constexpr bool native() const
Definition Asset.h:125
AccountID const & getIssuer() const
Definition Asset.cpp:21
A currency issued by an account.
Definition Issue.h:18
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:43
Number is a floating point type that can represent a wide range of values.
Definition Number.h:351
A view into a ledger.
Definition ReadView.h:41
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer 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:182
Asset const & asset() const
Definition STAmount.h:496
STAmount const & value() const noexcept
Definition STAmount.h:610
std::shared_ptr< STLedgerEntry const > const & ConstRef
std::shared_ptr< STLedgerEntry > const & Ref
beast::Journal const j_
Definition Transactor.h:164
ApplyView & view()
Definition Transactor.h:184
AccountID const accountID_
Definition Transactor.h:166
ApplyContext & ctx_
Definition Transactor.h:162
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static NotTEC preflight(PreflightContext const &ctx)
std::expected< std::pair< STAmount, STAmount >, TER > assetsToClawback(SLE::Ref vault, SLE::ConstRef sleShareIssuance, AccountID const &holder, STAmount const &clawbackAmount)
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
static TER preclaim(PreclaimContext const &ctx)
TER doApply() override
T make_pair(T... args)
constexpr Zero kZero
Definition Zero.h:30
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Definition Indexes.cpp:591
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:567
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::optional< STAmount > sharesToAssetsWithdraw(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const &shares, WaiveUnrealizedLoss waive=WaiveUnrealizedLoss::No)
From the perspective of a vault, return the number of assets to give the depositor when they redeem a...
bool isSoleShareholder(ReadView const &view, AccountID const &account, SLE::ConstRef issuance)
Returns true iff account holds all of the vault's outstanding shares — i.e.
bool debitIsNonZeroDust(Asset const &asset, Number const &total, Number const &amount)
Returns true if debiting amount from total (the current value of a vault's sfAssetsTotal or sfAssetsA...
std::optional< STAmount > assetsToSharesWithdraw(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::No, WaiveUnrealizedLoss waive=WaiveUnrealizedLoss::No)
From the perspective of a vault, return the number of shares to demand from the depositor when they a...
STAmount clawbackAmount(SLE::ConstRef vault, std::optional< STAmount > const &maybeAmount, AccountID const &account)
bool isXRP(AccountID const &c)
Definition AccountID.h:84
TER removeEmptyHolding(ApplyViewContext ctx, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
@ tefINTERNAL
Definition TER.h:168
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
std::expected< STAmount, TER > clampToAssetsTotalScale(SLE::ConstRef vault, STAmount const &delta)
Adjusts a requested asset change (delta) to match the decimal scale of the updated total vault assets...
std::string transToken(TER code)
Definition TER.cpp:257
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:614
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, SLE::Ref sponsorSle={}, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
bool isPseudoAccount(SLE::const_pointer sleAcct)
Returns true if and only if sleAcct is a pseudo-account of any kind (i.e.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
@ temMALFORMED
Definition TER.h:75
@ temBAD_AMOUNT
Definition TER.h:77
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecWRONG_ASSET
Definition TER.h:368
@ tecPSEUDO_ACCOUNT
Definition TER.h:370
@ tecNO_ENTRY
Definition TER.h:314
@ tecPATH_DRY
Definition TER.h:302
@ tecOBJECT_NOT_FOUND
Definition TER.h:334
@ tecINTERNAL
Definition TER.h:318
@ tecPRECISION_LOSS
Definition TER.h:371
@ tecLIMIT_EXCEEDED
Definition TER.h:369
@ tecNO_PERMISSION
Definition TER.h:313
@ tecHAS_OBLIGATIONS
Definition TER.h:325
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
@ tesSUCCESS
Definition TER.h:250
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
beast::Journal const j
Definition Transactor.h:100
State information when preflighting a tx.
Definition Transactor.h:39
beast::Journal const j
Definition Transactor.h:46
T unexpected(T... args)