mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-11 14:18:07 +00:00
Compare commits
27 Commits
develop
...
mvadari/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
256c1086c2 | ||
|
|
c65edeafe4 | ||
|
|
48a07702be | ||
|
|
1426579f03 | ||
|
|
3049cbf5e0 | ||
|
|
d2a7d4b0a6 | ||
|
|
a050c33365 | ||
|
|
a9e6b0a626 | ||
|
|
1a316007e0 | ||
|
|
d18aaab143 | ||
|
|
6994e386c6 | ||
|
|
404df08f1a | ||
|
|
068da5de11 | ||
|
|
f269a5810b | ||
|
|
b17e613e1f | ||
|
|
d4b462e560 | ||
|
|
8fa25ee5a7 | ||
|
|
7aa7f357e6 | ||
|
|
0a40c8e642 | ||
|
|
4d14f5ac77 | ||
|
|
517a2f2320 | ||
|
|
2a4f714370 | ||
|
|
2dfc9d0d02 | ||
|
|
63751ecbb0 | ||
|
|
6e7265e3d0 | ||
|
|
aad945ffb0 | ||
|
|
5252fbf8a2 |
@@ -79,13 +79,6 @@ isVaultPseudoAccountFrozen(
|
||||
MPTIssue const& mptShare,
|
||||
std::uint8_t depth);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isVaultPseudoAccountFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
SLE const& issuanceSle,
|
||||
std::uint8_t depth);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isLPTokenFrozen(
|
||||
ReadView const& view,
|
||||
|
||||
@@ -5,9 +5,14 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -48,6 +53,160 @@ public:
|
||||
: Base(keylet::mptokenIssuance(issuanceKey), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the issuance is locked (lsfMPTLocked).
|
||||
*
|
||||
* @return true if lsfMPTLocked is set, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen() const
|
||||
{
|
||||
return (*this)->isFlag(lsfMPTLocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the issuance's MaximumAmount, or the protocol maximum if the
|
||||
* field is absent. Never exceeds 2**63-1.
|
||||
*
|
||||
* @return the maximum amount that may be outstanding for this issuance.
|
||||
*/
|
||||
[[nodiscard]] std::int64_t
|
||||
maxAmount() const;
|
||||
|
||||
/**
|
||||
* Returns maxAmount() minus OutstandingAmount.
|
||||
*
|
||||
* OutstandingAmount may overflow, so the result might be negative, but it
|
||||
* is always <= |MaximumAmount - OutstandingAmount|.
|
||||
*
|
||||
* @return the amount of this issuance still available to be issued.
|
||||
*/
|
||||
[[nodiscard]] std::int64_t
|
||||
availableAmount() const
|
||||
{
|
||||
auto const max = maxAmount();
|
||||
auto const outstanding = (**this)[sfOutstandingAmount];
|
||||
return max - outstanding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MPT transfer fee as a Rate, in fractions of 1 billion
|
||||
* (a 1% fee is 1,010,000,000). Returns parity if TransferFee is absent.
|
||||
*
|
||||
* @return the transfer fee rate for this issuance.
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate() const;
|
||||
|
||||
/**
|
||||
* Returns tesSUCCESS if a holding of this issuance may be added:
|
||||
* tecOBJECT_NOT_FOUND if the issuance does not exist, tecNO_AUTH if
|
||||
* lsfMPTCanTransfer is not set.
|
||||
*
|
||||
* @return tesSUCCESS, tecOBJECT_NOT_FOUND, or tecNO_AUTH.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canAddHolding() const
|
||||
{
|
||||
if (!this->exists())
|
||||
{
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
}
|
||||
if (!(*this)->isFlag(lsfMPTCanTransfer))
|
||||
{
|
||||
return tecNO_AUTH;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the funds the issuer can still self-issue through an
|
||||
* issuer-owned MPT sell offer: availableAmount(), less amounts already
|
||||
* self-sold, as tracked by the view's balanceHookSelfIssueMPT().
|
||||
*
|
||||
* @return the amount the issuer may still self-issue.
|
||||
*/
|
||||
[[nodiscard]] STAmount
|
||||
issuerFundsToSelfIssue() const;
|
||||
|
||||
/**
|
||||
* Records @p amount of this MPT sold by the issuer through an
|
||||
* issuer-owned sell offer, via the view's issuerSelfDebitHookMPT().
|
||||
* See ApplyView::issuerSelfDebitHookMPT().
|
||||
*
|
||||
* @param amount the amount of this MPT the issuer sold.
|
||||
*/
|
||||
void
|
||||
issuerSelfDebitHook(std::uint64_t amount)
|
||||
requires Base::kIsWritable;
|
||||
|
||||
/**
|
||||
* Returns true if @p account is frozen through the vault behind this
|
||||
* issuance: if these are vault shares, checks whether the issuer (the
|
||||
* vault pseudo-account) or @p account is frozen for the vault's
|
||||
* underlying asset, recursing up to kMaxAssetCheckDepth. Returns false
|
||||
* if featureSingleAssetVault is disabled or the issuance does not exist.
|
||||
*
|
||||
* @param account the account to check for freeze.
|
||||
* @param depth Current recursion depth; callers outside the freeze
|
||||
* checks should pass 0.
|
||||
* @return true if @p account is frozen through the vault, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isVaultPseudoAccountFrozen(AccountID const& account, std::uint8_t depth) const;
|
||||
|
||||
/**
|
||||
* Returns true iff @p account holds all of this vault share issuance's
|
||||
* outstanding shares, i.e. is the sole remaining shareholder. Returns
|
||||
* false if the account holds no shares or fewer than the total
|
||||
* outstanding.
|
||||
*
|
||||
* @param account the account to check.
|
||||
* @return true if @p account is the sole shareholder, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isSoleShareholder(AccountID const& account) const;
|
||||
|
||||
/**
|
||||
* Check whether @p to may receive this MPT from @p from.
|
||||
*
|
||||
* Returns tecOBJECT_NOT_FOUND if the issuance does not exist. Otherwise
|
||||
* the check passes when @p waive is WaiveMPTCanTransfer::Yes, @p from or
|
||||
* @p to is the issuer, or lsfMPTCanTransfer is set. For vault shares
|
||||
* (sfReferenceHolding present) it recurses into the underlying asset's
|
||||
* transferability; @p depth is bounded at kMaxAssetCheckDepth.
|
||||
*
|
||||
* @param from the account the MPT is transferred from.
|
||||
* @param to the account the MPT is transferred to.
|
||||
* @param waive whether to waive the lsfMPTCanTransfer check.
|
||||
* @param depth Current recursion depth; callers outside the
|
||||
* transferability checks should pass 0.
|
||||
* @return tesSUCCESS if the transfer is allowed, tecNO_AUTH otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTransfer(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No,
|
||||
std::uint8_t depth = 0) const;
|
||||
|
||||
/**
|
||||
* Check whether this MPT may be traded on the DEX.
|
||||
*
|
||||
* Returns tecOBJECT_NOT_FOUND if the issuance does not exist and
|
||||
* tecNO_PERMISSION if lsfMPTCanTrade is not set. Vault shares recurse
|
||||
* into the underlying asset's tradability via sfReferenceHolding;
|
||||
* @p depth is bounded at kMaxAssetCheckDepth.
|
||||
*
|
||||
* @param depth Current recursion depth; callers outside the
|
||||
* tradability checks should pass 0.
|
||||
* @return tesSUCCESS if the MPT may be traded, tecOBJECT_NOT_FOUND, or
|
||||
* tecNO_PERMISSION otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTrade(std::uint8_t depth = 0) const;
|
||||
};
|
||||
|
||||
using MPTokenIssuanceEntryR = MPTokenIssuanceEntry<ReadView>;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -29,9 +30,6 @@ namespace xrpl {
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(SLE const& issuanceSle);
|
||||
|
||||
/**
|
||||
* Returns true if @p account's MPToken for @p mptIssue carries the
|
||||
* individual-lock flag (lsfMPTLocked).
|
||||
@@ -178,7 +176,7 @@ enforceMPTokenAuthorization(
|
||||
* @return The underlying Asset (MPTIssue or Issue).
|
||||
*/
|
||||
[[nodiscard]] Asset
|
||||
assetOfHolding(SLE const& sleShareIssuance, SLE const& sleHolding);
|
||||
assetOfHolding(MPTokenIssuanceEntryR const& sleShareIssuance, SLE const& sleHolding);
|
||||
|
||||
/**
|
||||
* Check whether @p to may receive the given MPT from @p from.
|
||||
@@ -318,9 +316,6 @@ maxMPTAmount(SLE const& sleIssuance);
|
||||
|
||||
// OutstandingAmount may overflow and available amount might be negative.
|
||||
// But available amount is always <= |MaximumAmount - OutstandingAmount|.
|
||||
std::int64_t
|
||||
availableMPTAmount(SLE const& sleIssuance);
|
||||
|
||||
std::int64_t
|
||||
availableMPTAmount(ReadView const& view, MPTID const& mptID);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -29,7 +30,10 @@ class STTx;
|
||||
* @return The number of shares, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const& assets);
|
||||
assetsToSharesDeposit(
|
||||
SLE::ConstRef vault,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& assets);
|
||||
|
||||
/**
|
||||
* From the perspective of a vault, return the number of assets to take from
|
||||
@@ -43,7 +47,10 @@ assetsToSharesDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount cons
|
||||
* @return The number of assets, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const& shares);
|
||||
sharesToAssetsDeposit(
|
||||
SLE::ConstRef vault,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& shares);
|
||||
|
||||
/**
|
||||
* Adjusts a requested asset change (`delta`) to match the decimal scale of the
|
||||
@@ -132,7 +139,7 @@ debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
SLE::ConstRef issuance,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate = TruncateShares::No,
|
||||
WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No);
|
||||
@@ -153,23 +160,10 @@ assetsToSharesWithdraw(
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
SLE::ConstRef issuance,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& shares,
|
||||
WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No);
|
||||
|
||||
/**
|
||||
* Returns true iff `account` holds all of the vault's outstanding shares —
|
||||
* i.e. is the sole remaining shareholder. Returns false if the account
|
||||
* holds no shares or fewer than the total outstanding.
|
||||
*
|
||||
* @param view The ledger view.
|
||||
* @param account The candidate sole shareholder.
|
||||
* @param issuance The MPTokenIssuance SLE for the vault's shares; provides
|
||||
* both the share MPTID and the outstanding-amount total.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isSoleShareholder(ReadView const& view, AccountID const& account, SLE::ConstRef issuance);
|
||||
|
||||
/**
|
||||
* Resolves a Vault's LEVersion, the single point every accounting touch
|
||||
* point should call to determine which recognition model (instant interest
|
||||
@@ -295,7 +289,7 @@ enum class SuppressExpired : bool { No = false, Yes = true };
|
||||
[[nodiscard]] TER
|
||||
checkVaultDomain(
|
||||
ReadView const& view,
|
||||
SLE::ConstRef issuance,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
AccountID const& subject,
|
||||
SuppressExpired suppressExpired);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -49,7 +50,7 @@ private:
|
||||
std::expected<std::pair<STAmount, STAmount>, TER>
|
||||
assetsToClawback(
|
||||
SLE::Ref vault,
|
||||
SLE::ConstRef sleShareIssuance,
|
||||
MPTokenIssuanceEntryR const& sleShareIssuance,
|
||||
AccountID const& holder,
|
||||
STAmount const& clawbackAmount);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -62,97 +63,6 @@ hasExpired(
|
||||
: view.parentCloseTime() > boundary;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::optional<bool>
|
||||
checkVaultPseudoAccountFrozenPreconditions(ReadView const& view, std::uint8_t depth)
|
||||
{
|
||||
if (!view.rules().enabled(featureSingleAssetVault))
|
||||
return false;
|
||||
|
||||
if (depth >= kMaxAssetCheckDepth)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::View::checkVaultPseudoAccountFrozenPreconditions : reached asset check depth");
|
||||
return true;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool
|
||||
isVaultPseudoAccountFrozenForIssuance(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
SLE const& issuanceSle,
|
||||
std::uint8_t depth)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
issuanceSle.getType() == ltMPTOKEN_ISSUANCE,
|
||||
"xrpl::isVaultPseudoAccountFrozenForIssuance : MPTokenIssuance SLE");
|
||||
|
||||
auto const issuer = issuanceSle.getAccountID(sfIssuer);
|
||||
|
||||
// Post-fixCleanup3_2_0: vault shares carry sfReferenceHolding pointing
|
||||
// to the vault pseudo's MPToken or RippleState for the underlying.
|
||||
// Read it to derive the underlying asset and recurse, skipping the
|
||||
// issuer-account-then-vault chain. Pre-amendment shares (no field)
|
||||
// fall back to the chain lookup below.
|
||||
if (issuanceSle.isFieldPresent(sfReferenceHolding))
|
||||
{
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked(issuanceSle.getFieldH256(sfReferenceHolding)));
|
||||
if (!sleHolding)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : dangling sfReferenceHolding");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return isAnyFrozen(
|
||||
view, {issuer, account}, assetOfHolding(issuanceSle, *sleHolding), depth + 1);
|
||||
}
|
||||
|
||||
auto const mptIssuer = view.read(keylet::account(issuer));
|
||||
if (mptIssuer == nullptr)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (!mptIssuer->isFieldPresent(sfVaultID))
|
||||
return false; // not a Vault pseudo-account, common case
|
||||
|
||||
auto const vault = view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
|
||||
if (vault == nullptr)
|
||||
{ // LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null vault");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool
|
||||
isVaultPseudoAccountFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
SLE const& issuanceSle,
|
||||
std::uint8_t depth)
|
||||
{
|
||||
if (auto const result = checkVaultPseudoAccountFrozenPreconditions(view, depth))
|
||||
return *result;
|
||||
|
||||
return isVaultPseudoAccountFrozenForIssuance(view, account, issuanceSle, depth);
|
||||
}
|
||||
|
||||
bool
|
||||
isVaultPseudoAccountFrozen(
|
||||
ReadView const& view,
|
||||
@@ -160,14 +70,8 @@ isVaultPseudoAccountFrozen(
|
||||
MPTIssue const& mptShare,
|
||||
std::uint8_t depth)
|
||||
{
|
||||
if (auto const result = checkVaultPseudoAccountFrozenPreconditions(view, depth))
|
||||
return *result;
|
||||
|
||||
auto const issuanceSle = view.read(keylet::mptokenIssuance(mptShare.getMptID()));
|
||||
if (issuanceSle == nullptr)
|
||||
return false; // zero MPToken won't block deletion of MPTokenIssuance
|
||||
|
||||
return isVaultPseudoAccountFrozenForIssuance(view, account, *issuanceSle, depth);
|
||||
return MPTokenIssuanceEntryR(mptShare.getMptID(), view)
|
||||
.isVaultPseudoAccountFrozen(account, depth);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
241
src/libxrpl/ledger/entries/MPTokenIssuanceEntry.cpp
Normal file
241
src/libxrpl/ledger/entries/MPTokenIssuanceEntry.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
std::int64_t
|
||||
MPTokenIssuanceEntry<ViewT>::maxAmount() const
|
||||
{
|
||||
return maxMPTAmount(**this);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
Rate
|
||||
MPTokenIssuanceEntry<ViewT>::transferRate() const
|
||||
{
|
||||
// fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000
|
||||
// For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000
|
||||
// which represents 50% of 1,000,000,000
|
||||
if ((*this)->isFieldPresent(sfTransferFee))
|
||||
{
|
||||
auto const fee = (*this)->getFieldU16(sfTransferFee);
|
||||
XRPL_ASSERT(fee <= kMaxTransferFee, "xrpl::transferRate : fee is too large");
|
||||
return Rate{1'000'000'000u + (10'000 * fee)};
|
||||
}
|
||||
|
||||
return kParityRate;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
STAmount
|
||||
MPTokenIssuanceEntry<ViewT>::issuerFundsToSelfIssue() const
|
||||
{
|
||||
MPTIssue const issue{(**this)[sfSequence], (**this)[sfIssuer]};
|
||||
auto const available = availableAmount();
|
||||
return this->readView().balanceHookSelfIssueMPT(issue, available);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
void
|
||||
MPTokenIssuanceEntry<ViewT>::issuerSelfDebitHook(std::uint64_t amount)
|
||||
requires Base::kIsWritable
|
||||
{
|
||||
MPTIssue const issue{(**this)[sfSequence], (**this)[sfIssuer]};
|
||||
auto const available = availableAmount();
|
||||
this->applyView().issuerSelfDebitHookMPT(issue, amount, available);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
bool
|
||||
MPTokenIssuanceEntry<ViewT>::isVaultPseudoAccountFrozen(
|
||||
AccountID const& account,
|
||||
std::uint8_t depth) const
|
||||
{
|
||||
auto const& view = this->readView();
|
||||
|
||||
if (!view.rules().enabled(featureSingleAssetVault))
|
||||
return false;
|
||||
|
||||
if (depth >= kMaxAssetCheckDepth)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::View::checkVaultPseudoAccountFrozenPreconditions : reached asset check depth");
|
||||
return true;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (!this->exists())
|
||||
return false; // zero MPToken won't block deletion of MPTokenIssuance
|
||||
|
||||
auto const issuer = (*this)->getAccountID(sfIssuer);
|
||||
|
||||
// Post-fixCleanup3_2_0: vault shares carry sfReferenceHolding pointing
|
||||
// to the vault pseudo's MPToken or RippleState for the underlying.
|
||||
// Read it to derive the underlying asset and recurse, skipping the
|
||||
// issuer-account-then-vault chain. Pre-amendment shares (no field)
|
||||
// fall back to the chain lookup below.
|
||||
if ((*this)->isFieldPresent(sfReferenceHolding))
|
||||
{
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked((*this)->getFieldH256(sfReferenceHolding)));
|
||||
if (!sleHolding)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : dangling sfReferenceHolding");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return isAnyFrozen(view, {issuer, account}, assetOfHolding(*this, *sleHolding), depth + 1);
|
||||
}
|
||||
|
||||
auto const mptIssuer = view.read(keylet::account(issuer));
|
||||
if (mptIssuer == nullptr)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (!mptIssuer->isFieldPresent(sfVaultID))
|
||||
return false; // not a Vault pseudo-account, common case
|
||||
|
||||
auto const vault = view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
|
||||
if (vault == nullptr)
|
||||
{ // LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null vault");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
bool
|
||||
MPTokenIssuanceEntry<ViewT>::isSoleShareholder(AccountID const& account) const
|
||||
{
|
||||
std::uint64_t const outstanding = (*this)->at(sfOutstandingAmount);
|
||||
if (outstanding == 0)
|
||||
return false;
|
||||
|
||||
auto const shareMPTID =
|
||||
makeMptID((*this)->getFieldU32(sfSequence), (*this)->getAccountID(sfIssuer));
|
||||
auto const sleToken = this->readView().read(keylet::mptoken(shareMPTID, account));
|
||||
if (!sleToken)
|
||||
return false; // LCOV_EXCL_LINE
|
||||
|
||||
return sleToken->getFieldU64(sfMPTAmount) == outstanding;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
TER
|
||||
MPTokenIssuanceEntry<ViewT>::canTransfer(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
WaiveMPTCanTransfer waive,
|
||||
std::uint8_t depth) const
|
||||
{
|
||||
if (!this->exists())
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto const& view = this->readView();
|
||||
|
||||
auto const issuer = (**this)[sfIssuer];
|
||||
if (waive == WaiveMPTCanTransfer::Yes || from == issuer || to == issuer)
|
||||
return tesSUCCESS;
|
||||
|
||||
if (!(*this)->isFlag(lsfMPTCanTransfer))
|
||||
return TER{tecNO_AUTH};
|
||||
|
||||
// Post-fixCleanup3_2_0: vault shares carry sfReferenceHolding pointing
|
||||
// to the vault pseudo's MPToken or RippleState for the underlying asset.
|
||||
// Third-party transfers inherit the underlying's transferability.
|
||||
// Issuer-involving transfers and waived callers returned tesSUCCESS above.
|
||||
//
|
||||
// The recursive call always passes WaiveMPTCanTransfer::No so that
|
||||
// a waived outer caller does not transitively unlock the underlying.
|
||||
if (view.rules().enabled(fixCleanup3_2_0) && (*this)->isFieldPresent(sfReferenceHolding))
|
||||
{
|
||||
// Defensive depth bound on the inheritance recursion. Unreachable
|
||||
// in practice (vault-of-vault-shares is forbidden at VaultCreate).
|
||||
if (depth >= kMaxAssetCheckDepth)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::MPTokenHelpers::canTransfer : reached asset check depth");
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked((*this)->getFieldH256(sfReferenceHolding)));
|
||||
if (!sleHolding)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
return xrpl::canTransfer(
|
||||
view, assetOfHolding(*this, *sleHolding), from, to, WaiveMPTCanTransfer::No, depth + 1);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
TER
|
||||
MPTokenIssuanceEntry<ViewT>::canTrade(std::uint8_t depth) const
|
||||
{
|
||||
if (!this->exists())
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
if (!(*this)->isFlag(lsfMPTCanTrade))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
auto const& view = this->readView();
|
||||
|
||||
// Post-fixCleanup3_2_0: vault shares inherit the underlying
|
||||
// asset's tradability. A share whose underlying has been
|
||||
// removed from trading cannot itself be placed on the DEX.
|
||||
if (view.rules().enabled(fixCleanup3_2_0) && (*this)->isFieldPresent(sfReferenceHolding))
|
||||
{
|
||||
// Defensive depth bound on the inheritance recursion.
|
||||
// Unreachable in practice (vault-of-vault-shares
|
||||
// forbidden at VaultCreate).
|
||||
if (depth >= kMaxAssetCheckDepth)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::MPTokenHelpers::canTrade : reached asset check depth");
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked((*this)->getFieldH256(sfReferenceHolding)));
|
||||
if (!sleHolding)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
return xrpl::canTrade(view, assetOfHolding(*this, *sleHolding), depth + 1);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template class MPTokenIssuanceEntry<ReadView>;
|
||||
template class MPTokenIssuanceEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -41,20 +42,11 @@ namespace xrpl {
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
|
||||
{
|
||||
if (auto const sle = view.read(keylet::mptokenIssuance(mptIssue.getMptID())))
|
||||
return isGlobalFrozen(*sle);
|
||||
if (auto const sle = MPTokenIssuanceEntryR(mptIssue.getMptID(), view))
|
||||
return sle.isGlobalFrozen();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
isGlobalFrozen(SLE const& issuanceSle)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
issuanceSle.getType() == ltMPTOKEN_ISSUANCE, "xrpl::isGlobalFrozen : MPTokenIssuance SLE");
|
||||
|
||||
return issuanceSle.isFlag(lsfMPTLocked);
|
||||
}
|
||||
|
||||
bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
|
||||
{
|
||||
@@ -94,20 +86,23 @@ isFrozen(ReadView const& view, AccountID const& account, SLE const& sle, std::ui
|
||||
XRPL_ASSERT(sle[sfAccount] == account, "xrpl::isFrozen : valid MPToken holder");
|
||||
|
||||
MPTID const mptID = sle[sfMPTokenIssuanceID];
|
||||
auto const issuanceSle = view.read(keylet::mptokenIssuance(mptID));
|
||||
MPTokenIssuanceEntryR const issuanceSle(mptID, view);
|
||||
|
||||
if ((issuanceSle && isGlobalFrozen(*issuanceSle)) || isIndividualFrozen(sle))
|
||||
if ((issuanceSle && issuanceSle.isGlobalFrozen()) || isIndividualFrozen(sle))
|
||||
return true;
|
||||
|
||||
if (issuanceSle)
|
||||
return isVaultPseudoAccountFrozen(view, account, *issuanceSle, depth);
|
||||
return issuanceSle.isVaultPseudoAccountFrozen(account, depth);
|
||||
|
||||
return isVaultPseudoAccountFrozen(view, account, MPTIssue{mptID}, depth);
|
||||
}
|
||||
|
||||
// Non-owning pointer (aliasing constructor): the caller owns sle for the
|
||||
// whole call, so this avoids copying the SLE.
|
||||
MPTokenIssuanceEntryR const issuanceSle(SLE::const_pointer(SLE::const_pointer{}, &sle), view);
|
||||
MPTIssue const mptIssue{sle[sfSequence], sle[sfIssuer]};
|
||||
return isGlobalFrozen(sle) || isIndividualFrozen(view, account, mptIssue) ||
|
||||
isVaultPseudoAccountFrozen(view, account, sle, depth);
|
||||
return issuanceSle.isGlobalFrozen() || isIndividualFrozen(view, account, mptIssue) ||
|
||||
issuanceSle.isVaultPseudoAccountFrozen(account, depth);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
@@ -117,8 +112,8 @@ isAnyFrozen(
|
||||
MPTIssue const& mptIssue,
|
||||
std::uint8_t depth)
|
||||
{
|
||||
auto const issuanceSle = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
if (issuanceSle && isGlobalFrozen(*issuanceSle))
|
||||
MPTokenIssuanceEntryR const issuanceSle(mptIssue.getMptID(), view);
|
||||
if (issuanceSle && issuanceSle.isGlobalFrozen())
|
||||
return true;
|
||||
|
||||
for (auto const& account : accounts)
|
||||
@@ -127,49 +122,25 @@ isAnyFrozen(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pass the issuance SLE when we have it to avoid re-reading it per account;
|
||||
// otherwise defer to the MPTIssue overload, which handles a missing issuance.
|
||||
auto const anyVaultFrozen = [&](auto const& shareOrIssuance) {
|
||||
return std::ranges::any_of(accounts, [&](auto const& account) {
|
||||
return isVaultPseudoAccountFrozen(view, account, shareOrIssuance, depth);
|
||||
});
|
||||
};
|
||||
|
||||
return issuanceSle ? anyVaultFrozen(*issuanceSle) : anyVaultFrozen(mptIssue);
|
||||
// Reuse the issuance entry to avoid re-reading it per account; the member
|
||||
// also handles a missing issuance.
|
||||
return std::ranges::any_of(accounts, [&](auto const& account) {
|
||||
return issuanceSle.isVaultPseudoAccountFrozen(account, depth);
|
||||
});
|
||||
}
|
||||
|
||||
Rate
|
||||
transferRate(ReadView const& view, MPTID const& issuanceID)
|
||||
{
|
||||
// fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000
|
||||
// For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000
|
||||
// which represents 50% of 1,000,000,000
|
||||
if (auto const sle = view.read(keylet::mptokenIssuance(issuanceID));
|
||||
sle && sle->isFieldPresent(sfTransferFee))
|
||||
{
|
||||
auto const fee = sle->getFieldU16(sfTransferFee);
|
||||
XRPL_ASSERT(fee <= kMaxTransferFee, "xrpl::transferRate : fee is too large");
|
||||
return Rate{1'000'000'000u + (10'000 * fee)};
|
||||
}
|
||||
|
||||
if (auto const sle = MPTokenIssuanceEntryR(issuanceID, view))
|
||||
return sle.transferRate();
|
||||
return kParityRate;
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
|
||||
{
|
||||
auto mptID = mptIssue.getMptID();
|
||||
auto issuance = view.read(keylet::mptokenIssuance(mptID));
|
||||
if (!issuance)
|
||||
{
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
}
|
||||
if (!issuance->isFlag(lsfMPTCanTransfer))
|
||||
{
|
||||
return tecNO_AUTH;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
return MPTokenIssuanceEntryR(mptIssue.getMptID(), view).canAddHolding();
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
@@ -181,7 +152,7 @@ addEmptyHolding(
|
||||
beast::Journal journal)
|
||||
{
|
||||
auto const& mptID = mptIssue.getMptID();
|
||||
auto const mpt = ctx.view.peek(keylet::mptokenIssuance(mptID));
|
||||
MPTokenIssuanceEntryW mpt(mptID, ctx.view);
|
||||
if (!mpt)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
// Unlike IOU addEmptyHolding (post-fixCleanup3_4_0), a locked issuance is
|
||||
@@ -264,7 +235,7 @@ authorizeMPToken(
|
||||
}
|
||||
|
||||
// Defensive check before we attempt to create MPToken for the issuer
|
||||
auto const mpt = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const mpt(mptIssuanceID, ctx.view);
|
||||
if (!mpt || mpt->getAccountID(sfIssuer) == account)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -291,7 +262,7 @@ authorizeMPToken(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
auto const sleMptIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleMptIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleMptIssuance)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -390,7 +361,7 @@ requireAuth(
|
||||
};
|
||||
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto const sleIssuance = view.read(mptID);
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptID, view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -485,7 +456,7 @@ enforceMPTokenAuthorization(
|
||||
XRPAmount const& priorBalance, // for MPToken authorization
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -578,21 +549,21 @@ enforceMPTokenAuthorization(
|
||||
}
|
||||
|
||||
[[nodiscard]] Asset
|
||||
assetOfHolding(SLE const& sleShareIssuance, SLE const& sleHolding)
|
||||
assetOfHolding(MPTokenIssuanceEntryR const& sleShareIssuance, SLE const& sleHolding)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
sleHolding.getType() == ltRIPPLE_STATE || sleHolding.getType() == ltMPTOKEN,
|
||||
"xrpl::assetOfHolding",
|
||||
"unexpected holding type");
|
||||
XRPL_ASSERT_PARTS(
|
||||
sleShareIssuance.getType() == ltMPTOKEN_ISSUANCE,
|
||||
sleShareIssuance->getType() == ltMPTOKEN_ISSUANCE,
|
||||
"xrpl::assetOfHolding",
|
||||
"not SLE MPTokenIssuance");
|
||||
|
||||
if (sleHolding.getType() == ltMPTOKEN)
|
||||
return MPTIssue{sleHolding.getFieldH192(sfMPTokenIssuanceID)};
|
||||
|
||||
auto const vaultPseudo = sleShareIssuance.at(sfIssuer);
|
||||
auto const vaultPseudo = sleShareIssuance->at(sfIssuer);
|
||||
auto const lowLimit = sleHolding.getFieldAmount(sfLowLimit);
|
||||
auto const highLimit = sleHolding.getFieldAmount(sfHighLimit);
|
||||
auto const& iouIssuer =
|
||||
@@ -609,52 +580,7 @@ canTransfer(
|
||||
WaiveMPTCanTransfer waive,
|
||||
std::uint8_t depth)
|
||||
{
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto const sleIssuance = view.read(mptID);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto const issuer = (*sleIssuance)[sfIssuer];
|
||||
if (waive == WaiveMPTCanTransfer::Yes || from == issuer || to == issuer)
|
||||
return tesSUCCESS;
|
||||
|
||||
if (!sleIssuance->isFlag(lsfMPTCanTransfer))
|
||||
return TER{tecNO_AUTH};
|
||||
|
||||
// Post-fixCleanup3_2_0: vault shares carry sfReferenceHolding pointing
|
||||
// to the vault pseudo's MPToken or RippleState for the underlying asset.
|
||||
// Third-party transfers inherit the underlying's transferability.
|
||||
// Issuer-involving transfers and waived callers returned tesSUCCESS above.
|
||||
//
|
||||
// The recursive call always passes WaiveMPTCanTransfer::No so that
|
||||
// a waived outer caller does not transitively unlock the underlying.
|
||||
if (view.rules().enabled(fixCleanup3_2_0) && sleIssuance->isFieldPresent(sfReferenceHolding))
|
||||
{
|
||||
// Defensive depth bound on the inheritance recursion. Unreachable
|
||||
// in practice (vault-of-vault-shares is forbidden at VaultCreate).
|
||||
if (depth >= kMaxAssetCheckDepth)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::MPTokenHelpers::canTransfer : reached asset check depth");
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding)));
|
||||
if (!sleHolding)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
return canTransfer(
|
||||
view,
|
||||
assetOfHolding(*sleIssuance, *sleHolding),
|
||||
from,
|
||||
to,
|
||||
WaiveMPTCanTransfer::No,
|
||||
depth + 1);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
return MPTokenIssuanceEntryR(mptIssue.getMptID(), view).canTransfer(from, to, waive, depth);
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -663,37 +589,7 @@ canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth)
|
||||
return asset.visit(
|
||||
[&](Issue const&) -> TER { return tesSUCCESS; },
|
||||
[&](MPTIssue const& mptIssue) -> TER {
|
||||
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
if (!sleIssuance->isFlag(lsfMPTCanTrade))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// Post-fixCleanup3_2_0: vault shares inherit the underlying
|
||||
// asset's tradability. A share whose underlying has been
|
||||
// removed from trading cannot itself be placed on the DEX.
|
||||
if (view.rules().enabled(fixCleanup3_2_0) &&
|
||||
sleIssuance->isFieldPresent(sfReferenceHolding))
|
||||
{
|
||||
// Defensive depth bound on the inheritance recursion.
|
||||
// Unreachable in practice (vault-of-vault-shares
|
||||
// forbidden at VaultCreate).
|
||||
if (depth >= kMaxAssetCheckDepth)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::MPTokenHelpers::canTrade : reached asset check depth");
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding)));
|
||||
if (!sleHolding)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
return canTrade(view, assetOfHolding(*sleIssuance, *sleHolding), depth + 1);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
return MPTokenIssuanceEntryR(mptIssue.getMptID(), view).canTrade(depth);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -718,7 +614,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
|
||||
{
|
||||
auto const mptIssue = amount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
MPTokenIssuanceEntryW sleIssuance(mptID, view);
|
||||
if (!sleIssuance)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "lockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID();
|
||||
@@ -801,7 +697,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
|
||||
sleIssuance->setFieldU64(sfLockedAmount, pay);
|
||||
}
|
||||
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -823,7 +719,7 @@ unlockEscrowMPT(
|
||||
auto const& issuer = netAmount.getIssuer();
|
||||
auto const& mptIssue = netAmount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
MPTokenIssuanceEntryW sleIssuance(mptID, view);
|
||||
if (!sleIssuance)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "unlockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID();
|
||||
@@ -859,7 +755,7 @@ unlockEscrowMPT(
|
||||
{
|
||||
sleIssuance->setFieldU64(sfLockedAmount, newLocked);
|
||||
}
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
|
||||
if (issuer != receiver)
|
||||
@@ -902,7 +798,7 @@ unlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
|
||||
if (issuer == sender)
|
||||
@@ -965,7 +861,7 @@ unlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -1047,21 +943,13 @@ maxMPTAmount(SLE const& sleIssuance)
|
||||
return sleIssuance[~sfMaximumAmount].value_or(kMaxMpTokenAmount);
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
availableMPTAmount(SLE const& sleIssuance)
|
||||
{
|
||||
auto const max = maxMPTAmount(sleIssuance);
|
||||
auto const outstanding = sleIssuance[sfOutstandingAmount];
|
||||
return max - outstanding;
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
availableMPTAmount(ReadView const& view, MPTID const& mptID)
|
||||
{
|
||||
auto const sle = view.read(keylet::mptokenIssuance(mptID));
|
||||
MPTokenIssuanceEntryR const sle(mptID, view);
|
||||
if (!sle)
|
||||
Throw<std::runtime_error>(transHuman(tecINTERNAL));
|
||||
return availableMPTAmount(*sle);
|
||||
return sle.availableAmount();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1082,18 +970,19 @@ issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue)
|
||||
{
|
||||
STAmount amount{issue};
|
||||
|
||||
auto const sle = view.read(keylet::mptokenIssuance(issue));
|
||||
MPTokenIssuanceEntryR const sle(issue.getMptID(), view);
|
||||
if (!sle)
|
||||
return amount;
|
||||
auto const available = availableMPTAmount(*sle);
|
||||
return view.balanceHookSelfIssueMPT(issue, available);
|
||||
return sle.issuerFundsToSelfIssue();
|
||||
}
|
||||
|
||||
void
|
||||
issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amount)
|
||||
{
|
||||
auto const available = availableMPTAmount(view, issue);
|
||||
view.issuerSelfDebitHookMPT(issue, amount, available);
|
||||
MPTokenIssuanceEntryW sle(issue.getMptID(), view);
|
||||
if (!sle)
|
||||
Throw<std::runtime_error>(transHuman(tecINTERNAL));
|
||||
sle.issuerSelfDebitHook(amount);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -424,13 +425,13 @@ accountHolds(
|
||||
{
|
||||
// if the account is the issuer, and the issuance exists, their limit is
|
||||
// the issuance limit minus the outstanding value
|
||||
auto const issuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
MPTokenIssuanceEntryR const issuance(mptIssue.getMptID(), view);
|
||||
|
||||
if (!issuance)
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
auto const available = availableMPTAmount(*issuance);
|
||||
auto const available = issuance.availableAmount();
|
||||
if (!mptokensV2)
|
||||
return STAmount{mptIssue, available};
|
||||
return view.balanceHookMPT(issuer, mptIssue, available);
|
||||
@@ -459,7 +460,7 @@ accountHolds(
|
||||
}
|
||||
else if (zeroIfUnauthorized == AuthHandling::ZeroIfUnauthorized)
|
||||
{
|
||||
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssue.getMptID(), view);
|
||||
|
||||
// if auth is enabled on the issuance and mpt is not authorized,
|
||||
// clear amount
|
||||
@@ -1217,13 +1218,13 @@ directSendNoFeeMPT(
|
||||
// Do not check MPT authorization here - it must have been checked earlier
|
||||
auto const mptID = keylet::mptokenIssuance(saAmount.get<MPTIssue>().getMptID());
|
||||
auto const& issuer = saAmount.getIssuer();
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
MPTokenIssuanceEntryW sleIssuance(mptID, view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto const maxAmount = maxMPTAmount(*sleIssuance);
|
||||
auto const maxAmount = sleIssuance.maxAmount();
|
||||
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const available = availableMPTAmount(*sleIssuance);
|
||||
auto const available = sleIssuance.availableAmount();
|
||||
auto const amt = saAmount.mpt().value();
|
||||
|
||||
if (uSenderID == issuer)
|
||||
@@ -1234,7 +1235,7 @@ directSendNoFeeMPT(
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
(*sleIssuance)[sfOutstandingAmount] += amt;
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1259,7 +1260,7 @@ directSendNoFeeMPT(
|
||||
if (outstanding >= amt)
|
||||
{
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - amt);
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1307,7 +1308,7 @@ directSendNoLimitMPT(
|
||||
// Safe to get MPT since directSendNoLimitMPT is only called by accountSendMPT
|
||||
auto const& issuer = saAmount.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptokenIssuance(saAmount.get<MPTIssue>().getMptID()));
|
||||
MPTokenIssuanceEntryR const sle(saAmount.get<MPTIssue>().getMptID(), view);
|
||||
if (!sle)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -1318,7 +1319,7 @@ directSendNoLimitMPT(
|
||||
if (uSenderID == issuer)
|
||||
{
|
||||
auto const sendAmount = saAmount.mpt().value();
|
||||
auto const maxAmount = maxMPTAmount(*sle);
|
||||
auto const maxAmount = sle.maxAmount();
|
||||
auto const outstanding = sle->getFieldU64(sfOutstandingAmount);
|
||||
auto const mptokensV2 = view.rules().enabled(featureMPTokensV2);
|
||||
allowOverflow = (allowOverflow == AllowMPTOverflow::Yes && mptokensV2)
|
||||
@@ -1364,7 +1365,7 @@ directSendNoLimitMultiMPT(
|
||||
{
|
||||
auto const& issuer = mptIssue.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
MPTokenIssuanceEntryR const sle(mptIssue.getMptID(), view);
|
||||
if (!sle)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -25,7 +25,10 @@
|
||||
namespace xrpl {
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const& assets)
|
||||
assetsToSharesDeposit(
|
||||
SLE::ConstRef vault,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& assets)
|
||||
{
|
||||
XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets");
|
||||
XRPL_ASSERT(
|
||||
@@ -49,7 +52,10 @@ assetsToSharesDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount cons
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const& shares)
|
||||
sharesToAssetsDeposit(
|
||||
SLE::ConstRef vault,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& shares)
|
||||
{
|
||||
XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares");
|
||||
XRPL_ASSERT(
|
||||
@@ -150,7 +156,7 @@ debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
SLE::ConstRef issuance,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate,
|
||||
WaiveUnrealizedLoss waive)
|
||||
@@ -177,7 +183,7 @@ assetsToSharesWithdraw(
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
SLE::ConstRef issuance,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
STAmount const& shares,
|
||||
WaiveUnrealizedLoss waive)
|
||||
{
|
||||
@@ -197,26 +203,6 @@ sharesToAssetsWithdraw(
|
||||
return assets;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isSoleShareholder(ReadView const& view, AccountID const& account, SLE::ConstRef issuance)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
issuance && issuance->getType() == ltMPTOKEN_ISSUANCE,
|
||||
"xrpl::isSoleShareholder : valid issuance SLE");
|
||||
|
||||
std::uint64_t const outstanding = issuance->at(sfOutstandingAmount);
|
||||
if (outstanding == 0)
|
||||
return false;
|
||||
|
||||
auto const shareMPTID =
|
||||
makeMptID(issuance->getFieldU32(sfSequence), issuance->getAccountID(sfIssuer));
|
||||
auto const sleToken = view.read(keylet::mptoken(shareMPTID, account));
|
||||
if (!sleToken)
|
||||
return false; // LCOV_EXCL_LINE
|
||||
|
||||
return sleToken->getFieldU64(sfMPTAmount) == outstanding;
|
||||
}
|
||||
|
||||
[[nodiscard]] VaultVersion
|
||||
getVaultVersion(SLE::ConstRef vault)
|
||||
{
|
||||
@@ -308,7 +294,7 @@ getVaultPhase(
|
||||
[[nodiscard]] TER
|
||||
checkVaultDomain(
|
||||
ReadView const& view,
|
||||
SLE::ConstRef issuance,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
AccountID const& subject,
|
||||
SuppressExpired suppressExpired)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -471,7 +472,7 @@ MPTEndpointStep<TDerived>::maxPaymentFlow(ReadView const& sb) const
|
||||
return {toAmount<MPTAmount>(maxFlow), DebtDirection::Redeems};
|
||||
|
||||
// From an issuer to a holder
|
||||
if (auto const sle = sb.read(keylet::mptokenIssuance(mptIssue_)))
|
||||
if (auto const sle = MPTokenIssuanceEntryR(mptIssue_.getMptID(), sb))
|
||||
{
|
||||
// If issuer is the source account, and it is direct payment then
|
||||
// MPTEndpointStep is the only step. Provide available maxFlow.
|
||||
@@ -483,7 +484,7 @@ MPTEndpointStep<TDerived>::maxPaymentFlow(ReadView const& sb) const
|
||||
// the previous step may issue or redeem. Allow OutstandingAmount
|
||||
// to temporarily overflow. Let the previous step figure out how
|
||||
// to limit the flow.
|
||||
std::int64_t const maxAmount = maxMPTAmount(*sle);
|
||||
std::int64_t const maxAmount = sle.maxAmount();
|
||||
return {MPTAmount{maxAmount}, DebtDirection::Issues};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -136,7 +137,7 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
|
||||
!sleIssuer->isFlag(lsfNoFreeze);
|
||||
},
|
||||
[&](MPTIssue const& issue) {
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(issue.getMptID()));
|
||||
MPTokenIssuanceEntryR const sleIssuance(issue.getMptID(), ctx.view);
|
||||
|
||||
return sleIssuance && sleIssuance->isFlag(lsfMPTCanClawback) &&
|
||||
sleIssuance->getAccountID(sfIssuer) == ctx.tx[sfAccount];
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
@@ -227,7 +228,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
auto clawbackDisabled = [&](Asset const& asset) -> TER {
|
||||
return asset.visit(
|
||||
[&](MPTIssue const& issue) -> TER {
|
||||
auto const sle = ctx.view.read(keylet::mptokenIssuance(issue.getMptID()));
|
||||
MPTokenIssuanceEntryR const sle(issue.getMptID(), ctx.view);
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
if (sle->isFlag(lsfMPTCanClawback))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
@@ -73,8 +74,7 @@ escrowCancelPreclaimHelper<MPTIssue>(
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntryR const sleIssuance(amount.get<MPTIssue>().getMptID(), ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
@@ -278,7 +279,7 @@ escrowCreatePreclaimHelper<MPTIssue>(
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntryR const sleIssuance(issuanceKey, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
@@ -179,8 +180,7 @@ escrowFinishPreclaimHelper<MPTIssue>(
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntryR const sleIssuance(amount.get<MPTIssue>().getMptID(), ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -218,8 +219,7 @@ preclaimHelper<MPTIssue>(
|
||||
SLE const& sleIssuer,
|
||||
STAmount const& clawAmount)
|
||||
{
|
||||
auto const issuanceKey = keylet::mptokenIssuance(clawAmount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntryR const sleIssuance(clawAmount.get<MPTIssue>().getMptID(), ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -154,7 +155,7 @@ preclaimHelper<MPTIssue>(
|
||||
STAmount const& clawAmount)
|
||||
{
|
||||
auto const issuanceKey = keylet::mptokenIssuance(clawAmount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntryR const sleIssuance(issuanceKey, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -66,7 +67,7 @@ ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
// Check if MPT issuance exists
|
||||
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -132,7 +133,7 @@ ConfidentialMPTClawback::doApply()
|
||||
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
|
||||
auto const holder = ctx_.tx[sfHolder];
|
||||
|
||||
auto sleIssuance = view().peek(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryW sleIssuance(mptIssuanceID, view(), j_);
|
||||
auto sleHolderMPToken = view().peek(keylet::mptoken(mptIssuanceID, holder));
|
||||
|
||||
if (!sleIssuance || !sleHolderMPToken)
|
||||
@@ -229,7 +230,7 @@ ConfidentialMPTClawback::doApply()
|
||||
(*sleIssuance)[sfOutstandingAmount] = oldOA - clawAmount;
|
||||
|
||||
view().update(sleHolderMPToken);
|
||||
view().update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -78,7 +79,7 @@ ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx)
|
||||
auto const amount = ctx.tx[sfMPTAmount];
|
||||
|
||||
// ensure that issuance exists
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(issuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(issuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -236,7 +237,7 @@ ConfidentialMPTConvert::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto sleIssuance = view().peek(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryW sleIssuance(mptIssuanceID, view(), j_);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -376,7 +377,7 @@ ConfidentialMPTConvert::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
view().update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
view().update(sleMptoken);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -70,7 +71,7 @@ ConfidentialMPTConvertBack::calculateBaseFee(ReadView const& view, STTx const& t
|
||||
static TER
|
||||
verifyProofs(
|
||||
STTx const& tx,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
MPTokenIssuanceEntryR const& issuance,
|
||||
std::shared_ptr<SLE const> const& mptoken)
|
||||
{
|
||||
if (!mptoken->isFieldPresent(sfHolderEncryptionKey))
|
||||
@@ -154,7 +155,7 @@ ConfidentialMPTConvertBack::preclaim(PreclaimContext const& ctx)
|
||||
auto const amount = ctx.tx[sfMPTAmount];
|
||||
|
||||
// ensure that issuance exists
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -255,7 +256,7 @@ ConfidentialMPTConvertBack::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto sleIssuance = view().peek(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryW sleIssuance(mptIssuanceID, view(), j_);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -333,7 +334,7 @@ ConfidentialMPTConvertBack::doApply()
|
||||
|
||||
incrementConfidentialVersion(*sleMptoken);
|
||||
|
||||
view().update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
view().update(sleMptoken);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -109,7 +110,7 @@ ConfidentialMPTHolderKeyUpdate::preclaim(PreclaimContext const& ctx)
|
||||
auto const account = ctx.tx[sfAccount];
|
||||
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
|
||||
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
auto const sleIssuance = MPTokenIssuanceEntryR(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -40,7 +41,7 @@ ConfidentialMPTMergeInbox::calculateBaseFee(ReadView const& view, STTx const& tx
|
||||
TER
|
||||
ConfidentialMPTMergeInbox::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(ctx.tx[sfMPTokenIssuanceID]));
|
||||
MPTokenIssuanceEntryR const sleIssuance(ctx.tx[sfMPTokenIssuanceID], ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -99,7 +100,7 @@ ConfidentialMPTMirrorUpdate::preclaim(PreclaimContext const& ctx)
|
||||
// The issuance must exist and have confidential balances enabled with a
|
||||
// registered issuer encryption key; otherwise there is no mirror to update.
|
||||
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -209,7 +210,7 @@ ConfidentialMPTMirrorUpdate::doApply()
|
||||
{
|
||||
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
|
||||
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, view());
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
@@ -102,7 +103,7 @@ verifySendProofs(
|
||||
PreclaimContext const& ctx,
|
||||
std::shared_ptr<SLE const> const& sleSenderMPToken,
|
||||
std::shared_ptr<SLE const> const& sleDestinationMPToken,
|
||||
std::shared_ptr<SLE const> const& sleIssuance)
|
||||
MPTokenIssuanceEntryR const& sleIssuance)
|
||||
{
|
||||
// Sanity check
|
||||
if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance)
|
||||
@@ -180,7 +181,7 @@ ConfidentialMPTSend::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
// Check if MPT issuance exists
|
||||
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -307,7 +308,7 @@ ConfidentialMPTSend::doApply()
|
||||
|
||||
auto sleSenderMPToken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
|
||||
auto sleDestinationMPToken = view().peek(keylet::mptoken(mptIssuanceID, destination));
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, view());
|
||||
|
||||
auto const sleDestAcct = view().read(keylet::account(destination));
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/tx/transactors/token/MPTokenAuthorize.h>
|
||||
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -37,7 +38,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const accountID = ctx.tx[sfAccount];
|
||||
auto const holderID = ctx.tx[~sfHolder];
|
||||
auto const sleMptIssuance = ctx.view.read(keylet::mptokenIssuance(ctx.tx[sfMPTokenIssuanceID]));
|
||||
MPTokenIssuanceEntryR const sleMptIssuance(ctx.tx[sfMPTokenIssuanceID], ctx.view);
|
||||
|
||||
// if non-issuer account submits this tx, then they are trying either:
|
||||
// 1. Unauthorize/delete MPToken
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -154,7 +155,8 @@ MPTokenIssuanceCreate::create(
|
||||
if (!ownerNode)
|
||||
return std::unexpected(tecDIR_FULL); // LCOV_EXCL_LINE
|
||||
|
||||
auto mptIssuance = std::make_shared<SLE>(mptIssuanceKeylet);
|
||||
MPTokenIssuanceEntryW mptIssuance(mptIssuanceKeylet, ctx.view, journal);
|
||||
mptIssuance.newSLE();
|
||||
(*mptIssuance)[sfFlags] = args.flags & ~tfUniversal;
|
||||
(*mptIssuance)[sfIssuer] = args.account;
|
||||
(*mptIssuance)[sfOutstandingAmount] = 0;
|
||||
@@ -195,9 +197,9 @@ MPTokenIssuanceCreate::create(
|
||||
(*mptIssuance)[sfReferenceHolding] = *args.referenceHolding;
|
||||
}
|
||||
|
||||
addSponsorToLedgerEntry(mptIssuance, sponsorSle);
|
||||
addSponsorToLedgerEntry(mptIssuance.mutableRawSle(), sponsorSle);
|
||||
|
||||
ctx.view.insert(mptIssuance);
|
||||
mptIssuance.insert();
|
||||
}
|
||||
|
||||
// Update owner count.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h>
|
||||
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -21,7 +23,7 @@ TER
|
||||
MPTokenIssuanceDestroy::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// ensure that issuance exists
|
||||
auto const sleMPT = ctx.view.read(keylet::mptokenIssuance(ctx.tx[sfMPTokenIssuanceID]));
|
||||
MPTokenIssuanceEntryR const sleMPT(ctx.tx[sfMPTokenIssuanceID], ctx.view);
|
||||
if (!sleMPT)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -42,15 +44,15 @@ MPTokenIssuanceDestroy::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
MPTokenIssuanceDestroy::doApply()
|
||||
{
|
||||
auto const mpt = view().peek(keylet::mptokenIssuance(ctx_.tx[sfMPTokenIssuanceID]));
|
||||
MPTokenIssuanceEntryW mpt(ctx_.tx[sfMPTokenIssuanceID], view(), j_);
|
||||
if (accountID_ != mpt->getAccountID(sfIssuer))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (!view().dirRemove(keylet::ownerDir(accountID_), (*mpt)[sfOwnerNode], mpt->key(), false))
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
decreaseOwnerCountForObject(view(), accountID_, mpt, 1, j_);
|
||||
view().erase(mpt);
|
||||
decreaseOwnerCountForObject(view(), accountID_, mpt.mutableRawSle(), 1, j_);
|
||||
mpt.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -143,7 +144,7 @@ TER
|
||||
MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// ensure that issuance exists
|
||||
auto const sleMptIssuance = ctx.view.read(keylet::mptokenIssuance(ctx.tx[sfMPTokenIssuanceID]));
|
||||
MPTokenIssuanceEntryR const sleMptIssuance(ctx.tx[sfMPTokenIssuanceID], ctx.view);
|
||||
if (!sleMptIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
@@ -87,7 +88,7 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
auto const holder = ctx.tx[sfHolder];
|
||||
auto const maybeAmount = ctx.tx[~sfAmount];
|
||||
auto const mptIssuanceID = vault->at(sfShareMPTID);
|
||||
auto const sleShareIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleShareIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleShareIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -192,8 +193,8 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
return vaultAsset.visit(
|
||||
[&](MPTIssue const& issue) -> TER {
|
||||
auto const mptIssue = ctx.view.read(keylet::mptokenIssuance(issue.getMptID()));
|
||||
if (mptIssue == nullptr)
|
||||
MPTokenIssuanceEntryR const mptIssue(issue.getMptID(), ctx.view);
|
||||
if (!mptIssue)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
if (!mptIssue->isFlag(lsfMPTCanClawback))
|
||||
@@ -233,7 +234,7 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
std::expected<std::pair<STAmount, STAmount>, TER>
|
||||
VaultClawback::assetsToClawback(
|
||||
SLE::Ref vault,
|
||||
SLE::ConstRef sleShareIssuance,
|
||||
MPTokenIssuanceEntryR const& sleShareIssuance,
|
||||
AccountID const& holder,
|
||||
STAmount const& clawbackAmount)
|
||||
{
|
||||
@@ -274,8 +275,7 @@ VaultClawback::assetsToClawback(
|
||||
{
|
||||
// Do not discount a sole holder's shares: clawing back AssetsAvailable
|
||||
// at the discounted rate can burn every share while loan assets remain.
|
||||
auto const waiveUnrealizedLoss =
|
||||
fix340Enabled && isSoleShareholder(view(), holder, sleShareIssuance)
|
||||
auto const waiveUnrealizedLoss = fix340Enabled && sleShareIssuance.isSoleShareholder(holder)
|
||||
? WaiveUnrealizedLoss::Yes
|
||||
: WaiveUnrealizedLoss::No;
|
||||
|
||||
@@ -395,7 +395,7 @@ VaultClawback::doApply()
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const mptIssuanceID = *vault->at(sfShareMPTID);
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, view());
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -66,7 +67,7 @@ VaultDelete::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
// Verify we can destroy MPTokenIssuance
|
||||
auto const sleMPT = ctx.view.read(keylet::mptokenIssuance(vault->at(sfShareMPTID)));
|
||||
MPTokenIssuanceEntryR const sleMPT(vault->at(sfShareMPTID), ctx.view);
|
||||
|
||||
if (!sleMPT)
|
||||
{
|
||||
@@ -121,7 +122,7 @@ VaultDelete::doApply()
|
||||
// Destroy the share issuance. Do not use MPTokenIssuanceDestroy for this,
|
||||
// no special logic needed. First run few checks, duplicated from preclaim.
|
||||
auto const shareMPTID = *vault->at(sfShareMPTID);
|
||||
auto const mpt = view().peek(keylet::mptokenIssuance(shareMPTID));
|
||||
MPTokenIssuanceEntryW mpt(shareMPTID, view(), j_);
|
||||
if (!mpt)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -155,9 +156,9 @@ VaultDelete::doApply()
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
decreaseOwnerCountForObject(view(), pseudoAcct, mpt, 1, j_);
|
||||
decreaseOwnerCountForObject(view(), pseudoAcct, mpt.mutableRawSle(), 1, j_);
|
||||
|
||||
view().erase(mpt);
|
||||
mpt.erase();
|
||||
|
||||
// The pseudo-account's directory should have been deleted already.
|
||||
if (view().peek(keylet::ownerDir(pseudoID)))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
@@ -141,7 +142,7 @@ VaultDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -253,7 +254,7 @@ VaultDeposit::doApply()
|
||||
|
||||
// Make sure the depositor can hold shares.
|
||||
auto const mptIssuanceID = (*vault)[sfShareMPTID];
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, view());
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -75,7 +76,7 @@ VaultSet::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
auto const mptIssuanceID = (*vault)[sfShareMPTID];
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, ctx.view);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -130,7 +131,7 @@ VaultSet::doApply()
|
||||
auto const vaultAsset = vault->at(sfAsset);
|
||||
|
||||
auto const mptIssuanceID = (*vault)[sfShareMPTID];
|
||||
auto const sleIssuance = view().peek(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryW sleIssuance(mptIssuanceID, view(), j_);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -164,7 +165,7 @@ VaultSet::doApply()
|
||||
{
|
||||
sleIssuance->makeFieldAbsent(sfDomainID);
|
||||
}
|
||||
view().update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
|
||||
// Note, we must update Vault object even if only DomainID is being updated
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -38,13 +39,16 @@ VaultWithdraw::checkExtraFeatures(PreflightContext const& ctx)
|
||||
}
|
||||
|
||||
static WaiveUnrealizedLoss
|
||||
shouldWaiveWithdrawal(ReadView const& view, AccountID const& account, SLE::ConstRef issuance)
|
||||
shouldWaiveWithdrawal(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTokenIssuanceEntryR const& issuance)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
issuance && issuance->getType() == ltMPTOKEN_ISSUANCE,
|
||||
"xrpl::shouldWaiveWithdrawal : valid issuance sle");
|
||||
|
||||
return view.rules().enabled(fixCleanup3_2_0) && isSoleShareholder(view, account, issuance)
|
||||
return view.rules().enabled(fixCleanup3_2_0) && issuance.isSoleShareholder(account)
|
||||
? WaiveUnrealizedLoss::Yes
|
||||
: WaiveUnrealizedLoss::No;
|
||||
}
|
||||
@@ -150,7 +154,7 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
// to the equivalent asset amount before checking withdrawal
|
||||
// limits. Pre-amendment the limit check was skipped for
|
||||
// share-denominated withdrawals.
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(vaultShare));
|
||||
MPTokenIssuanceEntryR const sleIssuance(vaultShare, ctx.view);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -227,7 +231,7 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
if (fix340Enabled && vault->isFlag(lsfVaultPrivate) && dstAcct != account &&
|
||||
dstAcct != vaultAsset.getIssuer())
|
||||
{
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(vaultShare));
|
||||
MPTokenIssuanceEntryR const sleIssuance(vaultShare, ctx.view);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -284,7 +288,7 @@ VaultWithdraw::doApply()
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const mptIssuanceID = *((*vault)[sfShareMPTID]);
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntryR const sleIssuance(mptIssuanceID, view());
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/entries/MPTokenIssuanceEntry.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -1522,7 +1523,7 @@ private:
|
||||
// assuming an exact 90/10 split holds under truncation.
|
||||
auto const maybeSharesDestroyed = assetsToSharesWithdraw(
|
||||
vaultBefore,
|
||||
issuanceBefore,
|
||||
MPTokenIssuanceEntryR(issuanceBefore, *env.current()),
|
||||
setup.usd(9'000).value(),
|
||||
TruncateShares::Yes,
|
||||
WaiveUnrealizedLoss::Yes);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <xrpl/ledger/entries/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
|
||||
#include <xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -76,12 +77,22 @@ template class SLEBase<ApplyView>;
|
||||
#pragma push_macro("LEDGER_ENTRY")
|
||||
#undef LEDGER_ENTRY
|
||||
|
||||
// No explicit instantiation here, neither a definition nor a declaration.
|
||||
// A definition would instantiate an entry a second time once its member
|
||||
// bodies live in its own .cpp (MSVC rejects that with C4661 under /WX), and a
|
||||
// declaration (`extern template`) stops clang from emitting the inline
|
||||
// constructors of the header-only entries at -O0, which leaves them undefined
|
||||
// at link time in the debug builds. Naming the class in a static_assert is
|
||||
// enough to instantiate its definition, which is all this exhaustiveness
|
||||
// check needs; the per-type suites exercise the members.
|
||||
#define LEDGER_ENTRY(tag, value, name, ...) \
|
||||
template class name##Entry<ReadView>; \
|
||||
template class name##Entry<ApplyView>; \
|
||||
static_assert( \
|
||||
name##Entry<ReadView>::kEntryType == tag && name##Entry<ApplyView>::kEntryType == tag, \
|
||||
#name "Entry must be bound to " #tag);
|
||||
#name "Entry must be bound to " #tag); \
|
||||
static_assert( \
|
||||
std::is_constructible_v<name##Entry<ReadView>, Keylet const&, ReadView const&> && \
|
||||
std::is_constructible_v<name##Entry<ApplyView>, Keylet const&, ApplyView&>, \
|
||||
#name "Entry must be constructible from a Keylet and a view");
|
||||
|
||||
#include <xrpl/protocol/detail/ledger_entries.macro>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user