1#include <xrpl/tx/transactors/vault/VaultClawback.h>
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/TokenHelpers.h>
10#include <xrpl/ledger/helpers/VaultHelpers.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/Asset.h>
13#include <xrpl/protocol/Feature.h>
14#include <xrpl/protocol/Indexes.h>
15#include <xrpl/protocol/Issue.h>
16#include <xrpl/protocol/LedgerFormats.h>
17#include <xrpl/protocol/MPTIssue.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STAmount.h>
20#include <xrpl/protocol/STLedgerEntry.h>
21#include <xrpl/protocol/STNumber.h>
22#include <xrpl/protocol/STTakesAsset.h>
23#include <xrpl/protocol/STTx.h>
24#include <xrpl/protocol/TER.h>
25#include <xrpl/protocol/XRPAmount.h>
26#include <xrpl/tx/Transactor.h>
37 if (ctx.
tx[sfVaultID] == beast::kZero)
39 JLOG(ctx.
j.
debug()) <<
"VaultClawback: zero/empty vault ID.";
43 auto const amount = ctx.
tx[~sfAmount];
47 if (*amount < beast::kZero)
51 if (
isXRP(amount->asset()))
53 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
71 if (account == vault->at(sfOwner))
84 Asset const vaultAsset = vault->at(sfAsset);
85 auto const account = ctx.
tx[sfAccount];
86 auto const holder = ctx.
tx[sfHolder];
87 auto const maybeAmount = ctx.
tx[~sfAmount];
88 auto const mptIssuanceID = vault->at(sfShareMPTID);
90 if (!sleShareIssuance)
93 JLOG(ctx.
j.
error()) <<
"VaultClawback: missing issuance of vault shares.";
101 if (!maybeAmount && !vaultAsset.
native() && vaultAsset.
getIssuer() == vault->at(sfOwner))
103 JLOG(ctx.
j.
debug()) <<
"VaultClawback: must specify amount when issuer is owner.";
113 if (amount.asset() == share)
116 if (account != vault->at(sfOwner))
118 JLOG(ctx.
j.
debug()) <<
"VaultClawback: only vault owner can clawback shares.";
122 auto const assetsTotal = vault->at(sfAssetsTotal);
123 auto const assetsAvailable = vault->at(sfAssetsAvailable);
124 auto const sharesTotal = sleShareIssuance->at(sfOutstandingAmount);
127 if (sharesTotal == 0 || (assetsTotal != 0 || assetsAvailable != 0))
129 JLOG(ctx.
j.
debug()) <<
"VaultClawback: vault owner can clawback shares only"
130 " when vault has no assets.";
135 if (amount != beast::kZero)
146 if (amount != sharesHeld)
148 JLOG(ctx.
j.
debug()) <<
"VaultClawback: vault owner must clawback all "
158 if (amount.asset() == vaultAsset)
163 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
170 JLOG(ctx.
j.
debug()) <<
"VaultClawback: only asset issuer can clawback asset.";
175 if (account == holder)
177 JLOG(ctx.
j.
debug()) <<
"VaultClawback: issuer cannot be the holder.";
181 return vaultAsset.
visit(
184 if (mptIssue ==
nullptr)
187 if (!mptIssue->isFlag(lsfMPTCanClawback))
189 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
201 JLOG(ctx.
j.
error()) <<
"VaultClawback: missing submitter account.";
206 if (!issuerSle->isFlag(lsfAllowTrustLineClawback) || issuerSle->isFlag(lsfNoFreeze))
208 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback "
221std::expected<std::pair<STAmount, STAmount>,
TER>
232 JLOG(
j_.error()) <<
"VaultClawback: asset mismatch in clawback.";
237 auto const assetsAvailable = vault->at(sfAssetsAvailable);
238 auto const mptIssuanceID = *vault->at(sfShareMPTID);
239 MPTIssue const share{mptIssuanceID};
265 auto const maybeAssets =
270 assetsRecovered = *maybeAssets;
274 auto const maybeShares =
278 sharesDestroyed = *maybeShares;
280 auto const maybeAssets =
284 assetsRecovered = *maybeAssets;
287 if (assetsRecovered > *assetsAvailable)
289 assetsRecovered = *assetsAvailable;
298 sharesDestroyed = *maybeShares;
301 auto const maybeAssets =
305 assetsRecovered = *maybeAssets;
306 if (assetsRecovered > *assetsAvailable)
309 JLOG(
j_.error()) <<
"VaultClawback: invalid rounding of shares.";
320 <<
"VaultClawback: overflow error with"
321 <<
" scale=" << (int)vault->at(sfScale).value()
322 <<
", assetsTotal=" << vault->at(sfAssetsTotal).value()
323 <<
", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount)
334 auto const& tx =
ctx_.tx;
339 auto const mptIssuanceID = *vault->at(sfShareMPTID);
344 JLOG(
j_.error()) <<
"VaultClawback: missing issuance of vault shares.";
348 MPTIssue const share{mptIssuanceID};
350 Asset const vaultAsset = vault->at(sfAsset);
353 auto assetsAvailable = vault->at(sfAssetsAvailable);
354 auto assetsTotal = vault->at(sfAssetsTotal);
356 [[maybe_unused]]
auto const lossUnrealized = vault->at(sfLossUnrealized);
358 lossUnrealized <= (assetsTotal - assetsAvailable),
359 "xrpl::VaultClawback::doApply : loss and assets do balance");
363 STAmount assetsRecovered = {vault->at(sfAsset)};
373 XRPL_ASSERT(amount.
asset() == vaultAsset,
"xrpl::VaultClawback::doApply : matching asset");
375 auto const clawbackParts =
assetsToClawback(vault, sleIssuance, holder, amount);
377 return clawbackParts.error();
379 assetsRecovered = clawbackParts->first;
380 sharesDestroyed = clawbackParts->second;
383 if (sharesDestroyed == beast::kZero)
386 assetsTotal -= assetsRecovered;
387 assetsAvailable -= assetsRecovered;
390 auto const& vaultAccount = vault->at(sfAccount);
400 if (holder != vault->at(sfOwner))
406 <<
"VaultClawback: removed empty MPToken for vault shares"
414 <<
"VaultClawback: failed to remove MPToken for vault shares"
424 if (assetsRecovered > beast::kZero)
436 assetsRecovered.
asset(),
442 JLOG(
j_.error()) <<
"VaultClawback: negative balance of vault assets.";
A generic endpoint for log messages.
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)
constexpr bool native() const
AccountID const & getIssuer() const
A currency issued by an account.
constexpr MPTID const & getMptID() const
Number is a floating point type that can represent a wide range of values.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
Asset const & asset() const
std::shared_ptr< STLedgerEntry > const & ref
std::shared_ptr< STLedgerEntry const > const & const_ref
AccountID const accountID_
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.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
std::expected< std::pair< STAmount, STAmount >, TER > assetsToClawback(SLE::ref vault, SLE::const_ref sleShareIssuance, AccountID const &holder, STAmount const &clawbackAmount)
Keylet mptokenIssuance(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.
std::optional< STAmount > assetsToSharesWithdraw(SLE::const_ref vault, SLE::const_ref 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...
bool isXRP(AccountID const &c)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
std::optional< STAmount > sharesToAssetsWithdraw(SLE::const_ref vault, SLE::const_ref 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...
std::string transToken(TER code)
std::string to_string(BaseUInt< Bits, Tag > const &a)
TERSubset< CanCvtToNotTEC > NotTEC
STAmount clawbackAmount(SLE::const_ref vault, std::optional< STAmount > const &maybeAmount, AccountID const &account)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.