1#include <xrpld/app/tx/detail/VaultClawback.h>
3#include <xrpl/beast/utility/instrumentation.h>
4#include <xrpl/ledger/View.h>
5#include <xrpl/protocol/AccountID.h>
6#include <xrpl/protocol/MPTIssue.h>
7#include <xrpl/protocol/SField.h>
8#include <xrpl/protocol/STAmount.h>
9#include <xrpl/protocol/STNumber.h>
10#include <xrpl/protocol/TER.h>
18 if (ctx.
tx[sfVaultID] == beast::zero)
20 JLOG(ctx.
j.
debug()) <<
"VaultClawback: zero/empty vault ID.";
24 auto const amount = ctx.
tx[~sfAmount];
28 if (*amount < beast::zero)
30 else if (
isXRP(amount->asset()))
32 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
50 if (account == vault->at(sfOwner))
63 Asset const vaultAsset = vault->at(sfAsset);
64 auto const account = ctx.
tx[sfAccount];
65 auto const holder = ctx.
tx[sfHolder];
66 auto const maybeAmount = ctx.
tx[~sfAmount];
67 auto const mptIssuanceID = vault->at(sfShareMPTID);
68 auto const sleShareIssuance =
70 if (!sleShareIssuance)
74 <<
"VaultClawback: missing issuance of vault shares.";
82 if (!maybeAmount && !vaultAsset.
native() &&
83 vaultAsset.
getIssuer() == vault->at(sfOwner))
86 <<
"VaultClawback: must specify amount when issuer is owner.";
96 if (amount.asset() == share)
99 if (account != vault->at(sfOwner))
102 <<
"VaultClawback: only vault owner can clawback shares.";
106 auto const assetsTotal = vault->at(sfAssetsTotal);
107 auto const assetsAvailable = vault->at(sfAssetsAvailable);
108 auto const sharesTotal = sleShareIssuance->at(sfOutstandingAmount);
111 if (sharesTotal == 0 || (assetsTotal != 0 || assetsAvailable != 0))
114 <<
"VaultClawback: vault owner can clawback shares only"
115 " when vault has no assets.";
120 if (amount != beast::zero)
131 if (amount != sharesHeld)
134 <<
"VaultClawback: vault owner must clawback all "
144 if (amount.asset() == vaultAsset)
149 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
157 <<
"VaultClawback: only asset issuer can clawback asset.";
162 if (account == holder)
165 <<
"VaultClawback: issuer cannot be the holder.";
173 auto const mptIssue =
175 if (mptIssue ==
nullptr)
179 mptIssue->getFieldU32(sfFlags);
182 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
189 auto const issuerSle =
195 <<
"VaultClawback: missing submitter account.";
201 issuerSle->getFieldU32(sfFlags);
205 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
230 JLOG(
j_.
error()) <<
"VaultClawback: asset mismatch in clawback.";
235 auto const assetsAvailable = vault->at(sfAssetsAvailable);
236 auto const mptIssuanceID = *vault->at(sfShareMPTID);
237 MPTIssue const share{mptIssuanceID};
248 auto const maybeAssets =
262 vault, sleShareIssuance, assetsRecovered);
265 sharesDestroyed = *maybeShares;
268 auto const maybeAssets =
272 assetsRecovered = *maybeAssets;
275 if (assetsRecovered > *assetsAvailable)
277 assetsRecovered = *assetsAvailable;
289 sharesDestroyed = *maybeShares;
293 vault, sleShareIssuance, sharesDestroyed);
296 assetsRecovered = *maybeAssets;
297 if (assetsRecovered > *assetsAvailable)
301 <<
"VaultClawback: invalid rounding of shares.";
312 <<
"VaultClawback: overflow error with"
313 <<
" scale=" << (int)vault->at(sfScale).value()
314 <<
", assetsTotal=" << vault->at(sfAssetsTotal).value()
315 <<
", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount)
331 auto const mptIssuanceID = *vault->at(sfShareMPTID);
336 JLOG(
j_.
error()) <<
"VaultClawback: missing issuance of vault shares.";
340 MPTIssue const share{mptIssuanceID};
342 Asset const vaultAsset = vault->at(sfAsset);
345 auto assetsAvailable = vault->at(sfAssetsAvailable);
346 auto assetsTotal = vault->at(sfAssetsTotal);
348 [[maybe_unused]]
auto const lossUnrealized = vault->at(sfLossUnrealized);
350 lossUnrealized <= (assetsTotal - assetsAvailable),
351 "xrpl::VaultClawback::doApply : loss and assets do balance");
355 STAmount assetsRecovered = {vault->at(sfAsset)};
358 if (
account_ == vault->at(sfOwner) && amount.asset() == share)
371 amount.asset() == vaultAsset,
372 "xrpl::VaultClawback::doApply : matching asset");
374 auto const clawbackParts =
377 return clawbackParts.error();
379 assetsRecovered = clawbackParts->first;
380 sharesDestroyed = clawbackParts->second;
383 if (sharesDestroyed == beast::zero)
386 assetsTotal -= assetsRecovered;
387 assetsAvailable -= assetsRecovered;
390 auto const& vaultAccount = vault->at(sfAccount);
405 if (holder != vault->at(sfOwner))
412 <<
"VaultClawback: removed empty MPToken for vault shares"
420 <<
"VaultClawback: failed to remove MPToken for vault shares"
430 if (assetsRecovered > beast::zero)
447 assetsRecovered.
asset(),
454 <<
"VaultClawback: negative balance of vault assets.";
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.
AccountID const & getIssuer() const
constexpr value_type const & value() const
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
Asset const & asset() const
STAmount const & value() const noexcept
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
Expected< std::pair< STAmount, STAmount >, TER > assetsToClawback(std::shared_ptr< SLE > const &vault, std::shared_ptr< SLE const > const &sleShareIssuance, AccountID const &holder, STAmount const &clawbackAmount)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool isXRP(AccountID const &c)
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
std::string to_string(base_uint< Bits, Tag > const &a)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
std::string transToken(TER code)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
TERSubset< CanCvtToTER > TER
bool isTesSuccess(TER x) noexcept
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
@ lsfAllowTrustLineClawback
STAmount clawbackAmount(std::shared_ptr< SLE const > const &vault, std::optional< STAmount > const &maybeAmount, AccountID const &account)
TERSubset< CanCvtToNotTEC > NotTEC
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.