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/Feature.h>
7#include <xrpl/protocol/MPTIssue.h>
8#include <xrpl/protocol/SField.h>
9#include <xrpl/protocol/STAmount.h>
10#include <xrpl/protocol/STNumber.h>
11#include <xrpl/protocol/TER.h>
12#include <xrpl/protocol/TxFlags.h>
19 if (ctx.
tx[sfVaultID] == beast::zero)
21 JLOG(ctx.
j.
debug()) <<
"VaultClawback: zero/empty vault ID.";
30 JLOG(ctx.
j.
debug()) <<
"VaultClawback: issuer cannot be holder.";
34 auto const amount = ctx.
tx[~sfAmount];
38 if (*amount < beast::zero)
40 else if (
isXRP(amount->asset()))
42 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
45 else if (amount->asset().getIssuer() != issuer)
48 <<
"VaultClawback: only asset issuer can clawback.";
63 auto account = ctx.
tx[sfAccount];
68 JLOG(ctx.
j.
error()) <<
"VaultClawback: missing issuer account.";
73 Asset const vaultAsset = vault->at(sfAsset);
74 if (
auto const amount = ctx.
tx[~sfAmount];
75 amount && vaultAsset != amount->asset())
80 JLOG(ctx.
j.
debug()) <<
"VaultClawback: cannot clawback XRP.";
83 else if (vaultAsset.
getIssuer() != account)
85 JLOG(ctx.
j.
debug()) <<
"VaultClawback: only asset issuer can clawback.";
94 if (mptIssue ==
nullptr)
97 std::uint32_t const issueFlags = mptIssue->getFieldU32(sfFlags);
101 <<
"VaultClawback: cannot clawback MPT vault asset.";
107 std::uint32_t const issuerFlags = issuer->getFieldU32(sfFlags);
112 <<
"VaultClawback: cannot clawback IOU vault asset.";
128 auto const mptIssuanceID = *((*vault)[sfShareMPTID]);
133 JLOG(
j_.
error()) <<
"VaultClawback: missing issuance of vault shares.";
138 Asset const vaultAsset = vault->at(sfAsset);
140 auto const maybeAmount = tx[~sfAmount];
143 return {sfAmount, vaultAsset, 0};
146 amount.
asset() == vaultAsset,
147 "ripple::VaultClawback::doApply : matching asset");
149 auto assetsAvailable = vault->at(sfAssetsAvailable);
150 auto assetsTotal = vault->at(sfAssetsTotal);
151 [[maybe_unused]]
auto const lossUnrealized = vault->at(sfLossUnrealized);
153 lossUnrealized <= (assetsTotal - assetsAvailable),
154 "ripple::VaultClawback::doApply : loss and assets do balance");
157 MPTIssue const share{mptIssuanceID};
162 if (amount == beast::zero)
172 auto const maybeAssets =
176 assetsRecovered = *maybeAssets;
180 assetsRecovered = amount;
182 auto const maybeShares =
186 sharesDestroyed = *maybeShares;
189 auto const maybeAssets =
193 assetsRecovered = *maybeAssets;
197 if (assetsRecovered > *assetsAvailable)
199 assetsRecovered = *assetsAvailable;
207 sharesDestroyed = *maybeShares;
210 auto const maybeAssets =
214 assetsRecovered = *maybeAssets;
215 if (assetsRecovered > *assetsAvailable)
219 <<
"VaultClawback: invalid rounding of shares.";
230 <<
"VaultClawback: overflow error with"
231 <<
" scale=" << (int)vault->at(sfScale).value()
232 <<
", assetsTotal=" << vault->at(sfAssetsTotal).value()
233 <<
", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
234 <<
", amount=" << amount.
value();
238 if (sharesDestroyed == beast::zero)
241 assetsTotal -= assetsRecovered;
242 assetsAvailable -= assetsRecovered;
245 auto const& vaultAccount = vault->at(sfAccount);
260 if (holder != vault->at(sfOwner))
267 <<
"VaultClawback: removed empty MPToken for vault shares"
275 <<
"VaultClawback: failed to remove MPToken for vault shares"
300 assetsRecovered.
asset(),
306 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.
constexpr TIss const & get() const
AccountID const & getIssuer() const
constexpr bool holds() const
A currency issued by an account.
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)
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.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
bool isXRP(AccountID const &c)
@ lsfAllowTrustLineClawback
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)
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 > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
std::string transToken(TER code)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
bool isTesSuccess(TER x) noexcept
std::string to_string(base_uint< Bits, Tag > const &a)
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, 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.