1#include <xrpl/beast/utility/instrumentation.h>
2#include <xrpl/ledger/View.h>
3#include <xrpl/ledger/helpers/TokenHelpers.h>
4#include <xrpl/ledger/helpers/VaultHelpers.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/STTakesAsset.h>
11#include <xrpl/protocol/TER.h>
12#include <xrpl/protocol/TxFlags.h>
13#include <xrpl/tx/transactors/vault/VaultClawback.h>
21 if (ctx.
tx[sfVaultID] == beast::zero)
23 JLOG(ctx.
j.
debug()) <<
"VaultClawback: zero/empty vault ID.";
27 auto const amount = ctx.
tx[~sfAmount];
31 if (*amount < beast::zero)
35 if (
isXRP(amount->asset()))
37 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
55 if (account == vault->at(sfOwner))
68 Asset const vaultAsset = vault->at(sfAsset);
69 auto const account = ctx.
tx[sfAccount];
70 auto const holder = ctx.
tx[sfHolder];
71 auto const maybeAmount = ctx.
tx[~sfAmount];
72 auto const mptIssuanceID = vault->at(sfShareMPTID);
74 if (!sleShareIssuance)
77 JLOG(ctx.
j.
error()) <<
"VaultClawback: missing issuance of vault shares.";
85 if (!maybeAmount && !vaultAsset.
native() && vaultAsset.
getIssuer() == vault->at(sfOwner))
87 JLOG(ctx.
j.
debug()) <<
"VaultClawback: must specify amount when issuer is owner.";
97 if (amount.asset() == share)
100 if (account != vault->at(sfOwner))
102 JLOG(ctx.
j.
debug()) <<
"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))
113 JLOG(ctx.
j.
debug()) <<
"VaultClawback: vault owner can clawback shares only"
114 " when vault has no assets.";
119 if (amount != beast::zero)
130 if (amount != sharesHeld)
132 JLOG(ctx.
j.
debug()) <<
"VaultClawback: vault owner must clawback all "
142 if (amount.asset() == vaultAsset)
147 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
154 JLOG(ctx.
j.
debug()) <<
"VaultClawback: only asset issuer can clawback asset.";
159 if (account == holder)
161 JLOG(ctx.
j.
debug()) <<
"VaultClawback: issuer cannot be the holder.";
170 if (mptIssue ==
nullptr)
173 std::uint32_t const issueFlags = mptIssue->getFieldU32(sfFlags);
174 if (!(issueFlags & lsfMPTCanClawback))
176 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
187 JLOG(ctx.
j.
error()) <<
"VaultClawback: missing submitter account.";
192 std::uint32_t const issuerFlags = issuerSle->getFieldU32(sfFlags);
193 if (!(issuerFlags & lsfAllowTrustLineClawback) || (issuerFlags & lsfNoFreeze))
195 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
220 JLOG(
j_.
error()) <<
"VaultClawback: asset mismatch in clawback.";
225 auto const assetsAvailable = vault->at(sfAssetsAvailable);
226 auto const mptIssuanceID = *vault->at(sfShareMPTID);
227 MPTIssue const share{mptIssuanceID};
250 auto const maybeShares =
254 sharesDestroyed = *maybeShares;
260 assetsRecovered = *maybeAssets;
263 if (assetsRecovered > *assetsAvailable)
265 assetsRecovered = *assetsAvailable;
274 sharesDestroyed = *maybeShares;
277 auto const maybeAssets =
281 assetsRecovered = *maybeAssets;
282 if (assetsRecovered > *assetsAvailable)
285 JLOG(
j_.
error()) <<
"VaultClawback: invalid rounding of shares.";
296 <<
"VaultClawback: overflow error with"
297 <<
" scale=" << (int)vault->at(sfScale).value()
298 <<
", assetsTotal=" << vault->at(sfAssetsTotal).value()
299 <<
", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount)
315 auto const mptIssuanceID = *vault->at(sfShareMPTID);
320 JLOG(
j_.
error()) <<
"VaultClawback: missing issuance of vault shares.";
324 MPTIssue const share{mptIssuanceID};
326 Asset const vaultAsset = vault->at(sfAsset);
329 auto assetsAvailable = vault->at(sfAssetsAvailable);
330 auto assetsTotal = vault->at(sfAssetsTotal);
332 [[maybe_unused]]
auto const lossUnrealized = vault->at(sfLossUnrealized);
334 lossUnrealized <= (assetsTotal - assetsAvailable),
335 "xrpl::VaultClawback::doApply : loss and assets do balance");
339 STAmount assetsRecovered = {vault->at(sfAsset)};
342 if (
account_ == vault->at(sfOwner) && amount.asset() == share)
354 XRPL_ASSERT(amount.asset() == vaultAsset,
"xrpl::VaultClawback::doApply : matching asset");
356 auto const clawbackParts =
assetsToClawback(vault, sleIssuance, holder, amount);
358 return clawbackParts.error();
360 assetsRecovered = clawbackParts->first;
361 sharesDestroyed = clawbackParts->second;
364 if (sharesDestroyed == beast::zero)
367 assetsTotal -= assetsRecovered;
368 assetsAvailable -= assetsRecovered;
371 auto const& vaultAccount = vault->at(sfAccount);
381 if (holder != vault->at(sfOwner))
387 <<
"VaultClawback: removed empty MPToken for vault shares"
395 <<
"VaultClawback: failed to remove MPToken for vault shares"
405 if (assetsRecovered > beast::zero)
417 assetsRecovered.
asset(),
423 JLOG(
j_.
error()) <<
"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
Number is a floating point type that can represent a wide range of values.
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)
std::string to_string(base_uint< Bits, Tag > const &a)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
std::string transToken(TER code)
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)
From the perspective of a vault, return the number of shares to demand from the depositor when they a...
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)
From the perspective of a vault, return the number of assets to give the depositor when they redeem a...
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
STAmount clawbackAmount(std::shared_ptr< SLE const > const &vault, std::optional< STAmount > const &maybeAmount, AccountID const &account)
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
TERSubset< CanCvtToNotTEC > NotTEC
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.