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/STTakesAsset.h>
11#include <xrpl/protocol/TER.h>
19 if (ctx.
tx[sfVaultID] == beast::zero)
21 JLOG(ctx.
j.
debug()) <<
"VaultClawback: zero/empty vault ID.";
25 auto const amount = ctx.
tx[~sfAmount];
29 if (*amount < beast::zero)
31 else if (
isXRP(amount->asset()))
33 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
51 if (account == vault->at(sfOwner))
64 Asset const vaultAsset = vault->at(sfAsset);
65 auto const account = ctx.
tx[sfAccount];
66 auto const holder = ctx.
tx[sfHolder];
67 auto const maybeAmount = ctx.
tx[~sfAmount];
68 auto const mptIssuanceID = vault->at(sfShareMPTID);
70 if (!sleShareIssuance)
73 JLOG(ctx.
j.
error()) <<
"VaultClawback: missing issuance of vault shares.";
81 if (!maybeAmount && !vaultAsset.
native() && vaultAsset.
getIssuer() == vault->at(sfOwner))
83 JLOG(ctx.
j.
debug()) <<
"VaultClawback: must specify amount when issuer is owner.";
93 if (amount.asset() == share)
96 if (account != vault->at(sfOwner))
98 JLOG(ctx.
j.
debug()) <<
"VaultClawback: only vault owner can clawback shares.";
102 auto const assetsTotal = vault->at(sfAssetsTotal);
103 auto const assetsAvailable = vault->at(sfAssetsAvailable);
104 auto const sharesTotal = sleShareIssuance->at(sfOutstandingAmount);
107 if (sharesTotal == 0 || (assetsTotal != 0 || assetsAvailable != 0))
109 JLOG(ctx.
j.
debug()) <<
"VaultClawback: vault owner can clawback shares only"
110 " when vault has no assets.";
115 if (amount != beast::zero)
121 if (amount != sharesHeld)
123 JLOG(ctx.
j.
debug()) <<
"VaultClawback: vault owner must clawback all "
133 if (amount.asset() == vaultAsset)
138 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
145 JLOG(ctx.
j.
debug()) <<
"VaultClawback: only asset issuer can clawback asset.";
150 if (account == holder)
152 JLOG(ctx.
j.
debug()) <<
"VaultClawback: issuer cannot be the holder.";
161 if (mptIssue ==
nullptr)
164 std::uint32_t const issueFlags = mptIssue->getFieldU32(sfFlags);
167 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
178 JLOG(ctx.
j.
error()) <<
"VaultClawback: missing submitter account.";
183 std::uint32_t const issuerFlags = issuerSle->getFieldU32(sfFlags);
186 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
211 JLOG(
j_.
error()) <<
"VaultClawback: asset mismatch in clawback.";
216 auto const assetsAvailable = vault->at(sfAssetsAvailable);
217 auto const mptIssuanceID = *vault->at(sfShareMPTID);
218 MPTIssue const share{mptIssuanceID};
222 auto const sharesDestroyed =
239 sharesDestroyed = *maybeShares;
245 assetsRecovered = *maybeAssets;
248 if (assetsRecovered > *assetsAvailable)
250 assetsRecovered = *assetsAvailable;
255 auto const maybeShares =
259 sharesDestroyed = *maybeShares;
265 assetsRecovered = *maybeAssets;
266 if (assetsRecovered > *assetsAvailable)
269 JLOG(
j_.
error()) <<
"VaultClawback: invalid rounding of shares.";
280 <<
"VaultClawback: overflow error with"
281 <<
" scale=" << (int)vault->at(sfScale).value()
282 <<
", assetsTotal=" << vault->at(sfAssetsTotal).value()
283 <<
", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount) <<
", amount=" <<
clawbackAmount.
value();
298 auto const mptIssuanceID = *vault->at(sfShareMPTID);
303 JLOG(
j_.
error()) <<
"VaultClawback: missing issuance of vault shares.";
307 MPTIssue const share{mptIssuanceID};
309 Asset const vaultAsset = vault->at(sfAsset);
312 auto assetsAvailable = vault->at(sfAssetsAvailable);
313 auto assetsTotal = vault->at(sfAssetsTotal);
315 [[maybe_unused]]
auto const lossUnrealized = vault->at(sfLossUnrealized);
317 lossUnrealized <= (assetsTotal - assetsAvailable),
"xrpl::VaultClawback::doApply : loss and assets do balance");
321 STAmount assetsRecovered = {vault->at(sfAsset)};
324 if (
account_ == vault->at(sfOwner) && amount.asset() == share)
331 XRPL_ASSERT(amount.asset() == vaultAsset,
"xrpl::VaultClawback::doApply : matching asset");
333 auto const clawbackParts =
assetsToClawback(vault, sleIssuance, holder, amount);
335 return clawbackParts.error();
337 assetsRecovered = clawbackParts->first;
338 sharesDestroyed = clawbackParts->second;
341 if (sharesDestroyed == beast::zero)
344 assetsTotal -= assetsRecovered;
345 assetsAvailable -= assetsRecovered;
348 auto const& vaultAccount = vault->at(sfAccount);
357 if (holder != vault->at(sfOwner))
362 <<
"VaultClawback: removed empty MPToken for vault shares"
370 <<
"VaultClawback: failed to remove MPToken for vault shares"
380 if (assetsRecovered > beast::zero)
391 assetsRecovered.
asset(),
397 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)
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.
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)
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
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)
TERSubset< CanCvtToNotTEC > NotTEC
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.