mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-21 19:18:22 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa43871c5e | ||
|
|
29fff28b39 | ||
|
|
0a0c761543 | ||
|
|
ac32719761 | ||
|
|
fe6ca095bf | ||
|
|
4d30c9a770 | ||
|
|
bf61b4956d | ||
|
|
991fff021c | ||
|
|
530e0e894a | ||
|
|
345a870b0d | ||
|
|
03438164d8 | ||
|
|
a382d1a70f | ||
|
|
71058accef | ||
|
|
8a1bfc322a | ||
|
|
f66808c64a |
No files matched your search
@@ -1,17 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/Units.h>
|
||||
|
||||
#include <expected>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Defined in VaultHelpers.h, which includes this header.
|
||||
enum class WaiveUnrealizedLoss : bool;
|
||||
|
||||
template <typename ViewT>
|
||||
class VaultEntry : public SLEBase<ViewT, ltVAULT>
|
||||
{
|
||||
@@ -38,6 +50,116 @@ public:
|
||||
: Base(keylet::vault(vaultID), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the Vault's LEVersion, the single point every accounting touch
|
||||
* point should call to determine which recognition model (instant
|
||||
* interest recognition vs. cash-basis) the Vault uses. Vaults created
|
||||
* before featureLendingProtocolV1_1 activated never have sfLEVersion set,
|
||||
* which resolves here to VaultVersion::Legacy.
|
||||
*
|
||||
* @return The Vault's LEVersion, or VaultVersion::Legacy if the field is
|
||||
* absent.
|
||||
*/
|
||||
[[nodiscard]] VaultVersion
|
||||
version() const;
|
||||
|
||||
/**
|
||||
* Resolves the Vault's VaultKind. Returns VaultKind::ClosedEnded when
|
||||
* sfVaultKind is present and equal to that value; anything else
|
||||
* (including an absent field or an unrecognised value) is treated as
|
||||
* VaultKind::OpenEnded.
|
||||
*
|
||||
* @return The Vault's VaultKind.
|
||||
*/
|
||||
[[nodiscard]] VaultKind
|
||||
kind() const;
|
||||
|
||||
/**
|
||||
* Returns the current lifecycle phase of the vault. Open-ended vaults are
|
||||
* always NoPhase. For closed-ended vaults the phase is derived from the
|
||||
* parent close time of the entry's view and the vault's immutable
|
||||
* SubscriptionDate and RedemptionDate.
|
||||
*
|
||||
* @return The vault's current VaultPhase.
|
||||
*/
|
||||
[[nodiscard]] VaultPhase
|
||||
phase() const;
|
||||
|
||||
/**
|
||||
* Adjusts a requested asset change (`delta`) to match the decimal scale of the
|
||||
* updated total vault assets. This ensures `sfAssetsTotal`, `sfAssetsAvailable`,
|
||||
* and the actual asset transfer change by the exact same representable amount.
|
||||
*
|
||||
* Rounding strategy:
|
||||
* - Debits (withdrawals): Rounds down `|delta|` on the new scale to prevent
|
||||
* paying out more than requested.
|
||||
* - Credits (deposits): Floors the resulting total asset balance and returns the
|
||||
* difference from the current total. This prevents crediting the vault with
|
||||
* more assets than the user deposited.
|
||||
*
|
||||
* Key rules:
|
||||
* - The returned magnitude never exceeds `|delta|`.
|
||||
* - Returns `tecPRECISION_LOSS` if the change is smaller than 1 ULP of the target scale
|
||||
* (prevents share operations when totals cannot change).
|
||||
* - For integer assets (XRP, MPT), rounding is a no-op.
|
||||
*
|
||||
* @param delta The requested signed change to sfAssetsTotal.
|
||||
* @return The rounded, positive magnitude, or `tecPRECISION_LOSS` if the
|
||||
* change is below representable precision.
|
||||
*/
|
||||
[[nodiscard]] std::expected<STAmount, TER>
|
||||
clampToAssetsTotalScale(STAmount const& delta) const;
|
||||
|
||||
/**
|
||||
* Returns the assets backing outstanding shares for a withdrawal:
|
||||
* sfAssetsTotal minus sfLossUnrealized, or sfAssetsTotal alone when the
|
||||
* unrealized loss is waived. Used by assetsToSharesWithdraw and
|
||||
* sharesToAssetsWithdraw as the numerator of the share/asset exchange
|
||||
* rate.
|
||||
*
|
||||
* @param waive Whether to skip subtracting the unrealized loss.
|
||||
* @return The assets backing outstanding shares for a withdrawal.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
assetsTotalForWithdrawal(WaiveUnrealizedLoss waive) const;
|
||||
|
||||
/**
|
||||
* Returns the scale of the vault's sfAssetsTotal for its asset, or
|
||||
* Number::kMinExponent - 1 if the entry does not exist.
|
||||
*
|
||||
* @return The scale of the vault's sfAssetsTotal, or Number::kMinExponent
|
||||
* - 1 if the entry does not exist.
|
||||
*/
|
||||
[[nodiscard]] int
|
||||
assetsTotalScale() const
|
||||
{
|
||||
if (!*this)
|
||||
return Number::kMinExponent - 1; // LCOV_EXCL_LINE
|
||||
return scale((*this)->at(sfAssetsTotal), (*this)->at(sfAsset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the minimum required broker cover, rounded consistently.
|
||||
* DebtTotal is a broker-level aggregate maintained at vault scale, so the
|
||||
* rounding must also use vault scale — never an individual loan's scale.
|
||||
*
|
||||
* @param debtTotal The broker's total outstanding debt, at vault scale.
|
||||
* @param coverRateMinimum The minimum cover rate to apply to debtTotal.
|
||||
* @return The minimum required broker cover, rounded up to vault scale.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum) const
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
*this && (*this)->getType() == ltVAULT,
|
||||
"xrpl::VaultEntry::minimumBrokerCover : valid Vault sle");
|
||||
NumberRoundModeGuard const mg(Number::RoundingMode::Upward);
|
||||
return roundToAsset(
|
||||
(*this)->at(sfAsset),
|
||||
tenthBipsOfValue(debtTotal, coverRateMinimum),
|
||||
assetsTotalScale());
|
||||
}
|
||||
};
|
||||
|
||||
using VaultEntryR = VaultEntry<ReadView>;
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
@@ -254,29 +254,6 @@ adjustImpreciseNumber(
|
||||
value = 0;
|
||||
}
|
||||
|
||||
inline int
|
||||
getAssetsTotalScale(SLE::ConstRef vaultSle)
|
||||
{
|
||||
if (!vaultSle)
|
||||
return Number::kMinExponent - 1; // LCOV_EXCL_LINE
|
||||
return scale(vaultSle->at(sfAssetsTotal), vaultSle->at(sfAsset));
|
||||
}
|
||||
|
||||
// Compute the minimum required broker cover, rounded consistently.
|
||||
// DebtTotal is a broker-level aggregate maintained at vault scale, so the
|
||||
// rounding must also use vault scale — never an individual loan's scale.
|
||||
inline Number
|
||||
minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::ConstRef vaultSle)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
vaultSle && vaultSle->getType() == ltVAULT, "xrpl::minimumBrokerCover : valid Vault sle");
|
||||
NumberRoundModeGuard const mg(Number::RoundingMode::Upward);
|
||||
return roundToAsset(
|
||||
vaultSle->at(sfAsset),
|
||||
tenthBipsOfValue(debtTotal, coverRateMinimum),
|
||||
getAssetsTotalScale(vaultSle));
|
||||
}
|
||||
|
||||
TER
|
||||
checkLoanGuards(
|
||||
Asset const& vaultAsset,
|
||||
@@ -380,26 +357,26 @@ loanPaymentDeltas(LoanPaymentParts const& parts);
|
||||
} // namespace cash_basis
|
||||
|
||||
// Public dispatchers: pick cash_basis:: if featureLendingProtocolV1_1 is
|
||||
// enabled AND the Vault's LEVersion (VaultHelpers::getVaultVersion) is
|
||||
// enabled AND the Vault's LEVersion (VaultEntry::version) is
|
||||
// VaultVersion::CashBasis, else instant_recognition::. These are the only entry points
|
||||
// transactors call.
|
||||
AccountingDeltas
|
||||
loanOriginationDeltas(
|
||||
SLE::ConstRef vaultSle,
|
||||
VaultEntryR const& vaultSle,
|
||||
Number const& principalRequested,
|
||||
Number const& interestDue);
|
||||
|
||||
bool
|
||||
loanOriginationExceedsVaultMaximum(
|
||||
SLE::ConstRef vaultSle,
|
||||
VaultEntryR const& vaultSle,
|
||||
Number const& vaultTotal,
|
||||
Number const& interestDue);
|
||||
|
||||
Number
|
||||
loanVaultExposure(SLE::ConstRef vaultSle, SLE::ConstRef loanSle);
|
||||
loanVaultExposure(VaultEntryR const& vaultSle, SLE::ConstRef loanSle);
|
||||
|
||||
AccountingDeltas
|
||||
loanPaymentDeltas(SLE::ConstRef vaultSle, LoanPaymentParts const& parts);
|
||||
loanPaymentDeltas(VaultEntryR const& vaultSle, LoanPaymentParts const& parts);
|
||||
|
||||
namespace detail {
|
||||
// These classes and functions should only be accessed by LendingHelper
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -10,7 +11,6 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -29,7 +29,7 @@ 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(VaultEntryR const& vault, SLE::ConstRef issuance, STAmount const& assets);
|
||||
|
||||
/**
|
||||
* From the perspective of a vault, return the number of assets to take from
|
||||
@@ -43,33 +43,7 @@ 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);
|
||||
|
||||
/**
|
||||
* Adjusts a requested asset change (`delta`) to match the decimal scale of the
|
||||
* updated total vault assets. This ensures `sfAssetsTotal`, `sfAssetsAvailable`,
|
||||
* and the actual asset transfer change by the exact same representable amount.
|
||||
*
|
||||
* Rounding strategy:
|
||||
* - Debits (withdrawals): Rounds down `|delta|` on the new scale to prevent
|
||||
* paying out more than requested.
|
||||
* - Credits (deposits): Floors the resulting total asset balance and returns the
|
||||
* difference from the current total. This prevents crediting the vault with
|
||||
* more assets than the user deposited.
|
||||
*
|
||||
* Key rules:
|
||||
* - The returned magnitude never exceeds `|delta|`.
|
||||
* - Returns `tecPRECISION_LOSS` if the change is smaller than 1 ULP of the target scale
|
||||
* (prevents share operations when totals cannot change).
|
||||
* - For integer assets (XRP, MPT), rounding is a no-op.
|
||||
*
|
||||
* @param vault The vault ledger entry.
|
||||
* @param delta The requested signed change to sfAssetsTotal.
|
||||
* @return The rounded, positive magnitude, or `tecPRECISION_LOSS` if the
|
||||
* change is below representable precision.
|
||||
*/
|
||||
[[nodiscard]] std::expected<STAmount, TER>
|
||||
clampToAssetsTotalScale(SLE::ConstRef vault, STAmount const& delta);
|
||||
sharesToAssetsDeposit(VaultEntryR const& vault, SLE::ConstRef issuance, STAmount const& shares);
|
||||
|
||||
/**
|
||||
* Controls whether to truncate shares instead of rounding.
|
||||
@@ -85,18 +59,6 @@ enum class TruncateShares : bool { No = false, Yes = true };
|
||||
*/
|
||||
enum class WaiveUnrealizedLoss : bool { No = false, Yes = true };
|
||||
|
||||
/**
|
||||
* Returns the assets backing outstanding shares for a withdrawal:
|
||||
* sfAssetsTotal minus sfLossUnrealized, or sfAssetsTotal alone when the
|
||||
* unrealized loss is waived. Used by assetsToSharesWithdraw and
|
||||
* sharesToAssetsWithdraw as the numerator of the share/asset exchange rate.
|
||||
*
|
||||
* @param vault The vault SLE.
|
||||
* @param waive Whether to skip subtracting the unrealized loss.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
assetsTotalForWithdrawal(SLE::ConstRef vault, WaiveUnrealizedLoss waive);
|
||||
|
||||
/**
|
||||
* Returns true if debiting `amount` from `total` (the current value of a
|
||||
* vault's sfAssetsTotal or sfAssetsAvailable) would canonicalize to the
|
||||
@@ -131,7 +93,7 @@ debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
VaultEntryR const& vault,
|
||||
SLE::ConstRef issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate = TruncateShares::No,
|
||||
@@ -152,7 +114,7 @@ assetsToSharesWithdraw(
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
VaultEntryR const& vault,
|
||||
SLE::ConstRef issuance,
|
||||
STAmount const& shares,
|
||||
WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No);
|
||||
@@ -170,35 +132,10 @@ sharesToAssetsWithdraw(
|
||||
[[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
|
||||
* recognition vs. cash-basis) a Vault uses. Vaults created before featureLendingProtocolV1_1
|
||||
* activated never have sfLEVersion set, which resolves here to
|
||||
* VaultVersion::Legacy.
|
||||
*
|
||||
* @param vault The vault SLE.
|
||||
*
|
||||
* @return The Vault's LEVersion, or VaultVersion::Legacy if the field is
|
||||
* absent.
|
||||
*/
|
||||
[[nodiscard]] VaultVersion
|
||||
getVaultVersion(SLE::ConstRef vault);
|
||||
|
||||
/**
|
||||
* Resolves the VaultKind of a vault SLE. Returns VaultKind::ClosedEnded when
|
||||
* sfVaultKind is present and equal to that value; anything else (including an
|
||||
* absent field or an unrecognised value) is treated as VaultKind::OpenEnded.
|
||||
*
|
||||
* @param vault The vault SLE.
|
||||
*/
|
||||
[[nodiscard]] VaultKind
|
||||
getVaultKind(SLE::ConstRef vault);
|
||||
|
||||
/**
|
||||
* Reads sfVaultKind from a transaction. An absent field resolves to
|
||||
* VaultKind::OpenEnded (matching the on-ledger default); any unrecognised
|
||||
* value is also treated as VaultKind::OpenEnded, mirroring the SLE overload.
|
||||
* value is also treated as VaultKind::OpenEnded, mirroring VaultEntry::kind().
|
||||
* Callers that need to reject out-of-range values (e.g. preflight) should
|
||||
* gate on isValidVaultKind() first.
|
||||
*
|
||||
@@ -231,23 +168,11 @@ isValidVaultKind(STTx const& tx);
|
||||
isValidClosedEndedGap(std::uint32_t sub, std::uint32_t red);
|
||||
|
||||
/**
|
||||
* Returns the current lifecycle phase of a vault. Open-ended
|
||||
* vaults are always NoPhase. For closed-ended vaults the phase is derived
|
||||
* from the parent ledger close time and the vault's immutable
|
||||
* SubscriptionDate and RedemptionDate.
|
||||
*
|
||||
* @param view The ledger view whose parent close time is used as the clock.
|
||||
* @param vault The vault SLE.
|
||||
*/
|
||||
[[nodiscard]] VaultPhase
|
||||
getVaultPhase(ReadView const& view, SLE::ConstRef vault);
|
||||
|
||||
/**
|
||||
* Raw-fields overload of getVaultPhase. Derives the phase from an already
|
||||
* Raw-fields variant of VaultEntry::phase(). Derives the phase from an already
|
||||
* decomposed vault snapshot: an absent or non-ClosedEnded @p vaultKind
|
||||
* resolves to VaultPhase::NoPhase; otherwise the phase is computed from
|
||||
* @p subscriptionDate and @p redemptionDate against the view's parent
|
||||
* close time using the same boundary semantics as the SLE overload
|
||||
* close time using the same boundary semantics as VaultEntry::phase()
|
||||
* (Subscription is inclusive of now == SubscriptionDate; Investment starts
|
||||
* strictly after).
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -44,7 +45,7 @@ public:
|
||||
ApplyView& view,
|
||||
SLE::Ref loanSle,
|
||||
SLE::Ref brokerSle,
|
||||
SLE::Ref vaultSle,
|
||||
VaultEntryW& vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
|
||||
@@ -55,7 +56,7 @@ public:
|
||||
impairLoan(
|
||||
ApplyView& view,
|
||||
SLE::Ref loanSle,
|
||||
SLE::Ref vaultSle,
|
||||
VaultEntryW& vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
unimpairLoan(
|
||||
ApplyView& view,
|
||||
SLE::Ref loanSle,
|
||||
SLE::Ref vaultSle,
|
||||
VaultEntryW& vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -48,7 +49,7 @@ public:
|
||||
private:
|
||||
std::expected<std::pair<STAmount, STAmount>, TER>
|
||||
assetsToClawback(
|
||||
SLE::Ref vault,
|
||||
VaultEntryW& vault,
|
||||
SLE::ConstRef 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/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -127,8 +128,8 @@ isVaultPseudoAccountFrozenForIssuance(
|
||||
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)
|
||||
VaultEntryR const vault(mptIssuer->getFieldH256(sfVaultID), view);
|
||||
if (!vault)
|
||||
{ // LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null vault");
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STNumber.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <expected>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
VaultVersion
|
||||
VaultEntry<ViewT>::version() const
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
*this && (*this)->getType() == ltVAULT, "xrpl::VaultEntry::version : valid Vault sle");
|
||||
if (!(*this)->isFieldPresent(sfLEVersion))
|
||||
return VaultVersion::Legacy;
|
||||
|
||||
auto const version = (*this)->at(sfLEVersion);
|
||||
if (version > std::to_underlying(VaultVersion::CashBasis))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::VaultEntry::version : invalid vault version");
|
||||
return VaultVersion::Legacy;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return static_cast<VaultVersion>(version);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
VaultKind
|
||||
VaultEntry<ViewT>::kind() const
|
||||
{
|
||||
XRPL_ASSERT(*this && (*this)->getType() == ltVAULT, "xrpl::VaultEntry::kind : valid Vault sle");
|
||||
auto const vaultKind = (*this)->at(~sfVaultKind);
|
||||
if (vaultKind && *vaultKind == std::to_underlying(VaultKind::ClosedEnded))
|
||||
return VaultKind::ClosedEnded;
|
||||
return VaultKind::OpenEnded;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
VaultPhase
|
||||
VaultEntry<ViewT>::phase() const
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
*this && (*this)->getType() == ltVAULT, "xrpl::VaultEntry::phase : valid Vault sle");
|
||||
return getVaultPhase(
|
||||
this->readView(),
|
||||
(**this)[~sfVaultKind],
|
||||
(**this)[~sfSubscriptionDate],
|
||||
(**this)[~sfRedemptionDate]);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
std::expected<STAmount, TER>
|
||||
VaultEntry<ViewT>::clampToAssetsTotalScale(STAmount const& delta) const
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
delta.asset() == (*this)->at(sfAsset),
|
||||
"xrpl::VaultEntry::clampToAssetsTotalScale : delta and vault asset match");
|
||||
|
||||
Asset const asset = (*this)->at(sfAsset);
|
||||
|
||||
STAmount magnitude = delta.negative() ? -delta : delta;
|
||||
if (asset.integral())
|
||||
{
|
||||
return magnitude;
|
||||
}
|
||||
Number const assetsTotal = (*this)->at(sfAssetsTotal);
|
||||
|
||||
// Calculate the scale after applying the delta using ToNearest rounding.
|
||||
// This aligns the delta with scale checks used by vault invariants.
|
||||
int const postScale = [&] {
|
||||
NumberRoundModeGuard const rg(Number::RoundingMode::ToNearest);
|
||||
return scale(assetsTotal + delta, asset);
|
||||
}();
|
||||
|
||||
STAmount actualDelta;
|
||||
if (delta.negative())
|
||||
{
|
||||
// For withdrawals (debits), floor the magnitude to the target scale
|
||||
// to ensure exact grid alignment without paying out extra assets.
|
||||
actualDelta = roundToScale(magnitude, postScale, Number::RoundingMode::Downward);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For deposits (credits), derive actualDelta from the floored posterior total.
|
||||
// This prevents grid alignment issues from crediting the vault more than deposited.
|
||||
//
|
||||
// Sum using Downward rounding so intermediate precision doesn't round up
|
||||
// and exceed the original requested amount.
|
||||
Number const posterior = [&] {
|
||||
NumberRoundModeGuard const rg(Number::RoundingMode::Downward);
|
||||
return assetsTotal + magnitude;
|
||||
}();
|
||||
|
||||
Number const roundedPosterior =
|
||||
roundToAsset(asset, posterior, postScale, Number::RoundingMode::Downward);
|
||||
actualDelta = STAmount{asset, roundedPosterior - assetsTotal};
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
abs(actualDelta) <= abs(delta),
|
||||
"xrpl::VaultEntry::clampToAssetsTotalScale : actual delta smaller or equal to calculated "
|
||||
"delta");
|
||||
|
||||
// Reject changes below scale precision (1 ULP) to prevent share balance changes
|
||||
// without corresponding asset movements.
|
||||
if (actualDelta <= beast::kZero)
|
||||
return std::unexpected(tecPRECISION_LOSS);
|
||||
|
||||
return actualDelta;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
Number
|
||||
VaultEntry<ViewT>::assetsTotalForWithdrawal(WaiveUnrealizedLoss waive) const
|
||||
{
|
||||
Number assetTotal = (*this)->at(sfAssetsTotal);
|
||||
if (waive == WaiveUnrealizedLoss::No)
|
||||
assetTotal -= (*this)->at(sfLossUnrealized);
|
||||
return assetTotal;
|
||||
}
|
||||
|
||||
template class VaultEntry<ReadView>;
|
||||
template class VaultEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -103,7 +104,7 @@ getLoanDefaultFreezeExemptAccounts(ReadView const& view, STTx const& tx)
|
||||
if (!brokerSle)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vaultSle = view.read(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntryR const vaultSle(brokerSle->at(sfVaultID), view);
|
||||
if (!vaultSle)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -253,16 +254,16 @@ namespace {
|
||||
// VaultVersion::CashBasis). Vaults created before activation keep instant
|
||||
// interest recognition forever, even after the amendment later turns on.
|
||||
bool
|
||||
cashBasisEnabled(SLE::ConstRef vaultSle)
|
||||
cashBasisEnabled(VaultEntryR const& vaultSle)
|
||||
{
|
||||
return getVaultVersion(vaultSle) == VaultVersion::CashBasis;
|
||||
return vaultSle.version() == VaultVersion::CashBasis;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AccountingDeltas
|
||||
loanOriginationDeltas(
|
||||
SLE::ConstRef vaultSle,
|
||||
VaultEntryR const& vaultSle,
|
||||
Number const& principalRequested,
|
||||
Number const& interestDue)
|
||||
{
|
||||
@@ -273,7 +274,7 @@ loanOriginationDeltas(
|
||||
|
||||
bool
|
||||
loanOriginationExceedsVaultMaximum(
|
||||
SLE::ConstRef vaultSle,
|
||||
VaultEntryR const& vaultSle,
|
||||
Number const& vaultTotal,
|
||||
Number const& interestDue)
|
||||
{
|
||||
@@ -288,14 +289,14 @@ loanOriginationExceedsVaultMaximum(
|
||||
}
|
||||
|
||||
Number
|
||||
loanVaultExposure(SLE::ConstRef vaultSle, SLE::ConstRef loanSle)
|
||||
loanVaultExposure(VaultEntryR const& vaultSle, SLE::ConstRef loanSle)
|
||||
{
|
||||
return cashBasisEnabled(vaultSle) ? cash_basis::loanVaultExposure(loanSle)
|
||||
: instant_recognition::loanVaultExposure(loanSle);
|
||||
}
|
||||
|
||||
AccountingDeltas
|
||||
loanPaymentDeltas(SLE::ConstRef vaultSle, LoanPaymentParts const& parts)
|
||||
loanPaymentDeltas(VaultEntryR const& vaultSle, LoanPaymentParts const& parts)
|
||||
{
|
||||
return cashBasisEnabled(vaultSle) ? cash_basis::loanPaymentDeltas(parts)
|
||||
: instant_recognition::loanPaymentDeltas(parts);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -421,7 +422,7 @@ requireAuth(
|
||||
|
||||
if (sleIssuer->isFieldPresent(sfVaultID))
|
||||
{
|
||||
auto const sleVault = view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
|
||||
VaultEntryR const sleVault(sleIssuer->getFieldH256(sfVaultID), view);
|
||||
if (!sleVault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -18,14 +18,13 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const& assets)
|
||||
assetsToSharesDeposit(VaultEntryR const& vault, SLE::ConstRef issuance, STAmount const& assets)
|
||||
{
|
||||
XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets");
|
||||
XRPL_ASSERT(
|
||||
@@ -49,7 +48,7 @@ assetsToSharesDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount cons
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount const& shares)
|
||||
sharesToAssetsDeposit(VaultEntryR const& vault, SLE::ConstRef issuance, STAmount const& shares)
|
||||
{
|
||||
XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares");
|
||||
XRPL_ASSERT(
|
||||
@@ -71,74 +70,6 @@ sharesToAssetsDeposit(SLE::ConstRef vault, SLE::ConstRef issuance, STAmount cons
|
||||
return assets;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<STAmount, TER>
|
||||
clampToAssetsTotalScale(SLE::ConstRef vault, STAmount const& delta)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
delta.asset() == vault->at(sfAsset),
|
||||
"xrpl::clampToAssetsTotalScale : delta and vault asset match");
|
||||
|
||||
Asset const asset = vault->at(sfAsset);
|
||||
|
||||
STAmount magnitude = delta.negative() ? -delta : delta;
|
||||
if (asset.integral())
|
||||
{
|
||||
return magnitude;
|
||||
}
|
||||
Number const assetsTotal = vault->at(sfAssetsTotal);
|
||||
|
||||
// Calculate the scale after applying the delta using ToNearest rounding.
|
||||
// This aligns the delta with scale checks used by vault invariants.
|
||||
int const postScale = [&] {
|
||||
NumberRoundModeGuard const rg(Number::RoundingMode::ToNearest);
|
||||
return scale(assetsTotal + delta, asset);
|
||||
}();
|
||||
|
||||
STAmount actualDelta;
|
||||
if (delta.negative())
|
||||
{
|
||||
// For withdrawals (debits), floor the magnitude to the target scale
|
||||
// to ensure exact grid alignment without paying out extra assets.
|
||||
actualDelta = roundToScale(magnitude, postScale, Number::RoundingMode::Downward);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For deposits (credits), derive actualDelta from the floored posterior total.
|
||||
// This prevents grid alignment issues from crediting the vault more than deposited.
|
||||
//
|
||||
// Sum using Downward rounding so intermediate precision doesn't round up
|
||||
// and exceed the original requested amount.
|
||||
Number const posterior = [&] {
|
||||
NumberRoundModeGuard const rg(Number::RoundingMode::Downward);
|
||||
return assetsTotal + magnitude;
|
||||
}();
|
||||
|
||||
Number const roundedPosterior =
|
||||
roundToAsset(asset, posterior, postScale, Number::RoundingMode::Downward);
|
||||
actualDelta = STAmount{asset, roundedPosterior - assetsTotal};
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
abs(actualDelta) <= abs(delta),
|
||||
"xrpl::clampToAssetsTotalScale : actual delta smaller or equal to calculated delta");
|
||||
|
||||
// Reject changes below scale precision (1 ULP) to prevent share balance changes
|
||||
// without corresponding asset movements.
|
||||
if (actualDelta <= beast::kZero)
|
||||
return std::unexpected(tecPRECISION_LOSS);
|
||||
|
||||
return actualDelta;
|
||||
}
|
||||
|
||||
[[nodiscard]] Number
|
||||
assetsTotalForWithdrawal(SLE::ConstRef vault, WaiveUnrealizedLoss waive)
|
||||
{
|
||||
Number assetTotal = vault->at(sfAssetsTotal);
|
||||
if (waive == WaiveUnrealizedLoss::No)
|
||||
assetTotal -= vault->at(sfLossUnrealized);
|
||||
return assetTotal;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount)
|
||||
{
|
||||
@@ -149,7 +80,7 @@ debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
VaultEntryR const& vault,
|
||||
SLE::ConstRef issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate,
|
||||
@@ -162,7 +93,7 @@ assetsToSharesWithdraw(
|
||||
if (assets.negative() || assets.asset() != vault->at(sfAsset))
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
Number const assetTotal = assetsTotalForWithdrawal(vault, waive);
|
||||
Number const assetTotal = vault.assetsTotalForWithdrawal(waive);
|
||||
STAmount shares{vault->at(sfShareMPTID)};
|
||||
if (assetTotal == 0)
|
||||
return shares;
|
||||
@@ -176,7 +107,7 @@ assetsToSharesWithdraw(
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
SLE::ConstRef vault,
|
||||
VaultEntryR const& vault,
|
||||
SLE::ConstRef issuance,
|
||||
STAmount const& shares,
|
||||
WaiveUnrealizedLoss waive)
|
||||
@@ -188,7 +119,7 @@ sharesToAssetsWithdraw(
|
||||
if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
Number const assetTotal = assetsTotalForWithdrawal(vault, waive);
|
||||
Number const assetTotal = vault.assetsTotalForWithdrawal(waive);
|
||||
STAmount assets{vault->at(sfAsset)};
|
||||
if (assetTotal == 0)
|
||||
return assets;
|
||||
@@ -217,24 +148,6 @@ isSoleShareholder(ReadView const& view, AccountID const& account, SLE::ConstRef
|
||||
return sleToken->getFieldU64(sfMPTAmount) == outstanding;
|
||||
}
|
||||
|
||||
[[nodiscard]] VaultVersion
|
||||
getVaultVersion(SLE::ConstRef vault)
|
||||
{
|
||||
XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::getVaultVersion : valid Vault sle");
|
||||
if (!vault->isFieldPresent(sfLEVersion))
|
||||
return VaultVersion::Legacy;
|
||||
|
||||
auto const version = vault->at(sfLEVersion);
|
||||
if (version > std::to_underlying(VaultVersion::CashBasis))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::getVaultVersion : invalid vault version");
|
||||
return VaultVersion::Legacy;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return static_cast<VaultVersion>(version);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] VaultKind
|
||||
@@ -247,13 +160,6 @@ decodeVaultKind(std::optional<std::uint8_t> vaultKind)
|
||||
|
||||
} // namespace
|
||||
|
||||
[[nodiscard]] VaultKind
|
||||
getVaultKind(SLE::ConstRef vault)
|
||||
{
|
||||
XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::getVaultKind : valid Vault sle");
|
||||
return decodeVaultKind(vault->at(~sfVaultKind));
|
||||
}
|
||||
|
||||
[[nodiscard]] VaultKind
|
||||
getVaultKind(STTx const& tx)
|
||||
{
|
||||
@@ -278,14 +184,6 @@ isValidClosedEndedGap(std::uint32_t sub, std::uint32_t red)
|
||||
return r >= s + kMinInvestmentPeriod && r < s + kMaxInvestmentPeriod;
|
||||
}
|
||||
|
||||
[[nodiscard]] VaultPhase
|
||||
getVaultPhase(ReadView const& view, SLE::ConstRef vault)
|
||||
{
|
||||
XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::getVaultPhase : valid Vault sle");
|
||||
return getVaultPhase(
|
||||
view, (*vault)[~sfVaultKind], (*vault)[~sfSubscriptionDate], (*vault)[~sfRedemptionDate]);
|
||||
}
|
||||
|
||||
[[nodiscard]] VaultPhase
|
||||
getVaultPhase(
|
||||
ReadView const& view,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -160,7 +160,7 @@ ValidLoanBroker::finalize(
|
||||
roundToAsset(
|
||||
Asset{vault->at(sfAsset)},
|
||||
debtTotal,
|
||||
getAssetsTotalScale(vault),
|
||||
VaultEntryR(vault, view).assetsTotalScale(),
|
||||
Number::RoundingMode::TowardsZero) != beast::kZero)
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -75,7 +76,7 @@ ValidLoan::finalize(
|
||||
auto const vault = view.read(keylet::vault(broker->at(sfVaultID)));
|
||||
// We don't check for LendingProtocolV1_1 amendment because a ClosedEnded Vault will
|
||||
// not exist without the amendment enabled
|
||||
if (vault && getVaultKind(vault) == VaultKind::ClosedEnded)
|
||||
if (vault && VaultEntryR(vault, view).kind() == VaultKind::ClosedEnded)
|
||||
{
|
||||
std::uint32_t const startDate = after->at(sfStartDate);
|
||||
std::uint32_t const interval = after->at(sfPaymentInterval);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -161,15 +162,15 @@ determineClawAmount(
|
||||
SLE const& sleBroker,
|
||||
Asset const& vaultAsset,
|
||||
std::optional<STAmount> const& amount,
|
||||
SLE::ConstRef vaultSle,
|
||||
VaultEntryR const& vaultSle,
|
||||
Rules const& rules)
|
||||
{
|
||||
auto const maxClawAmount = [&]() {
|
||||
auto const minRequiredCover = [&]() {
|
||||
if (rules.enabled(fixCleanup3_2_0))
|
||||
{
|
||||
return minimumBrokerCover(
|
||||
sleBroker[sfDebtTotal], TenthBips32(sleBroker[sfCoverRateMinimum]), vaultSle);
|
||||
return vaultSle.minimumBrokerCover(
|
||||
sleBroker[sfDebtTotal], TenthBips32(sleBroker[sfCoverRateMinimum]));
|
||||
}
|
||||
|
||||
// Always round the minimum required up
|
||||
@@ -254,7 +255,7 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
auto const brokerPseudoAccountID = sleBroker->at(sfAccount);
|
||||
|
||||
auto const vault = ctx.view.read(keylet::vault(sleBroker->at(sfVaultID)));
|
||||
VaultEntryR const vault(sleBroker->at(sfVaultID), ctx.view);
|
||||
if (!vault)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -350,7 +351,7 @@ LoanBrokerCoverClawback::doApply()
|
||||
|
||||
auto const brokerPseudoID = *sleBroker->at(sfAccount);
|
||||
|
||||
auto const vault = view().read(keylet::vault(sleBroker->at(sfVaultID)));
|
||||
VaultEntryR const vault(sleBroker->at(sfVaultID), view());
|
||||
if (!vault)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -62,7 +63,7 @@ LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx)
|
||||
JLOG(ctx.j.warn()) << "Account is not the owner of the LoanBroker.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
auto const vault = ctx.view.read(keylet::vault(sleBroker->at(sfVaultID)));
|
||||
VaultEntryR const vault(sleBroker->at(sfVaultID), ctx.view);
|
||||
if (!vault)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -143,7 +144,7 @@ LoanBrokerCoverDeposit::doApply()
|
||||
if (!broker)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vault = view().read(keylet::vault(broker->at(sfVaultID)));
|
||||
VaultEntryR const vault(broker->at(sfVaultID), view());
|
||||
if (!vault)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
@@ -90,7 +91,7 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
JLOG(ctx.j.warn()) << "Account is not the owner of the LoanBroker.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
auto const vault = ctx.view.read(keylet::vault(sleBroker->at(sfVaultID)));
|
||||
VaultEntryR const vault(sleBroker->at(sfVaultID), ctx.view);
|
||||
if (!vault)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -172,8 +173,8 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
auto const minimumCover = [&]() {
|
||||
if (fix320Enabled)
|
||||
{
|
||||
return minimumBrokerCover(
|
||||
currentDebtTotal, TenthBips32{sleBroker->at(sfCoverRateMinimum)}, vault);
|
||||
return vault.minimumBrokerCover(
|
||||
currentDebtTotal, TenthBips32{sleBroker->at(sfCoverRateMinimum)});
|
||||
}
|
||||
|
||||
// Always round the minimum required up.
|
||||
@@ -218,7 +219,7 @@ LoanBrokerCoverWithdraw::doApply()
|
||||
if (!broker)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vault = view().read(keylet::vault(broker->at(sfVaultID)));
|
||||
VaultEntryR const vault(broker->at(sfVaultID), view());
|
||||
if (!vault)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -62,7 +63,7 @@ LoanBrokerDelete::preclaim(PreclaimContext const& ctx)
|
||||
return tecHAS_OBLIGATIONS;
|
||||
}
|
||||
|
||||
auto const vault = ctx.view.read(keylet::vault(sleBroker->at(sfVaultID)));
|
||||
VaultEntryR const vault(sleBroker->at(sfVaultID), ctx.view);
|
||||
if (!vault)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -77,7 +78,7 @@ LoanBrokerDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Any remaining debt should have been wiped out by the last Loan
|
||||
// Delete. This check is purely defensive.
|
||||
auto const scale = getAssetsTotalScale(vault);
|
||||
auto const scale = vault.assetsTotalScale();
|
||||
|
||||
auto const rounded =
|
||||
roundToAsset(asset, debtTotal, scale, Number::RoundingMode::TowardsZero);
|
||||
@@ -132,7 +133,7 @@ LoanBrokerDelete::doApply()
|
||||
if (!broker)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultID = broker->at(sfVaultID);
|
||||
auto const sleVault = view().read(keylet::vault(vaultID));
|
||||
VaultEntryR const sleVault(vaultID, view());
|
||||
if (!sleVault)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultPseudoID = sleVault->at(sfAccount);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -98,7 +99,7 @@ LoanBrokerSet::preclaim(PreclaimContext const& ctx)
|
||||
auto const account = tx[sfAccount];
|
||||
auto const vaultID = tx[sfVaultID];
|
||||
|
||||
auto const sleVault = ctx.view.read(keylet::vault(vaultID));
|
||||
VaultEntryR const sleVault(vaultID, ctx.view);
|
||||
if (!sleVault)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Vault does not exist.";
|
||||
@@ -154,7 +155,7 @@ LoanBrokerSet::preclaim(PreclaimContext const& ctx)
|
||||
// the constraint is enforced here, at the point where the vault
|
||||
// is first bound to the lending protocol.
|
||||
if (ctx.view.rules().enabled(featureLendingProtocolV1_1) &&
|
||||
getVaultKind(sleVault) != VaultKind::ClosedEnded)
|
||||
sleVault.kind() != VaultKind::ClosedEnded)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "LoanBroker requires a closed-ended Vault.";
|
||||
return tecNO_PERMISSION;
|
||||
@@ -204,7 +205,7 @@ LoanBrokerSet::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const vault = view.read(keylet::vault(broker->at(sfVaultID)));
|
||||
VaultEntryR const vault(broker->at(sfVaultID), view);
|
||||
if (!vault)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -223,7 +224,7 @@ LoanBrokerSet::doApply()
|
||||
{
|
||||
// Create a new LoanBroker pointing back to the given Vault
|
||||
auto const vaultID = tx[sfVaultID];
|
||||
auto const sleVault = view.read(keylet::vault(vaultID));
|
||||
VaultEntryR const sleVault(vaultID, view);
|
||||
if (!sleVault)
|
||||
{
|
||||
// This should be impossible
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/basics/Number.h> // IWYU pragma: keep
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -90,7 +91,7 @@ LoanDelete::doApply()
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const brokerPseudoAccount = brokerSle->at(sfAccount);
|
||||
|
||||
auto const vaultSle = view.peek(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntryW vaultSle(brokerSle->at(sfVaultID), view);
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultAsset = vaultSle->at(sfAsset);
|
||||
@@ -120,7 +121,7 @@ LoanDelete::doApply()
|
||||
roundToAsset(
|
||||
vaultSle->at(sfAsset),
|
||||
debtTotalProxy,
|
||||
getAssetsTotalScale(vaultSle),
|
||||
vaultSle.assetsTotalScale(),
|
||||
Number::RoundingMode::TowardsZero) == beast::kZero,
|
||||
"xrpl::LoanDelete::doApply",
|
||||
"last loan, remaining debt rounds to zero");
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -136,7 +137,7 @@ LoanManage::defaultLoan(
|
||||
ApplyView& view,
|
||||
SLE::Ref loanSle,
|
||||
SLE::Ref brokerSle,
|
||||
SLE::Ref vaultSle,
|
||||
VaultEntryW& vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j)
|
||||
{
|
||||
@@ -176,7 +177,7 @@ LoanManage::defaultLoan(
|
||||
// The vault may be at a different scale than the loan. Reduce rounding
|
||||
// errors during the accounting by rounding some of the values to that
|
||||
// scale.
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale = vaultSle.assetsTotalScale();
|
||||
|
||||
{
|
||||
// Decrease the Total Value of the Vault:
|
||||
@@ -238,7 +239,7 @@ LoanManage::defaultLoan(
|
||||
adjustImpreciseNumber(
|
||||
vaultLossUnrealizedProxy, -totalDefaultAmount, vaultAsset, vaultScale);
|
||||
}
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
}
|
||||
|
||||
// Update the LoanBroker object:
|
||||
@@ -287,7 +288,7 @@ TER
|
||||
LoanManage::impairLoan(
|
||||
ApplyView& view,
|
||||
SLE::Ref loanSle,
|
||||
SLE::Ref vaultSle,
|
||||
VaultEntryW& vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j)
|
||||
{
|
||||
@@ -304,7 +305,7 @@ LoanManage::impairLoan(
|
||||
// The vault may be at a different scale than the loan. Reduce rounding
|
||||
// errors during the accounting by rounding some of the values to that
|
||||
// scale.
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale = vaultSle.assetsTotalScale();
|
||||
|
||||
// Update the Vault object(set "paper loss")
|
||||
auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
|
||||
@@ -316,7 +317,7 @@ LoanManage::impairLoan(
|
||||
JLOG(j.warn()) << "Vault unrealized loss is too large, and will corrupt the vault.";
|
||||
return tecLIMIT_EXCEEDED;
|
||||
}
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
// Update the Loan object
|
||||
loanSle->setFlag(lsfLoanImpaired);
|
||||
@@ -339,14 +340,14 @@ LoanManage::impairLoan(
|
||||
LoanManage::unimpairLoan(
|
||||
ApplyView& view,
|
||||
SLE::Ref loanSle,
|
||||
SLE::Ref vaultSle,
|
||||
VaultEntryW& vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j)
|
||||
{
|
||||
// The vault may be at a different scale than the loan. Reduce rounding
|
||||
// errors during the accounting by rounding some of the values to that
|
||||
// scale.
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale = vaultSle.assetsTotalScale();
|
||||
|
||||
// Update the Vault object(clear "paper loss")
|
||||
auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
|
||||
@@ -361,7 +362,7 @@ LoanManage::unimpairLoan(
|
||||
// Reverse the "paper loss"
|
||||
adjustImpreciseNumber(vaultLossUnrealizedProxy, -lossReversed, vaultAsset, vaultScale);
|
||||
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
// Update the Loan object
|
||||
loanSle->clearFlag(lsfLoanImpaired);
|
||||
@@ -405,7 +406,7 @@ LoanManage::doApply()
|
||||
if (!brokerSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vaultSle = view.peek(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntryW vaultSle(brokerSle->at(sfVaultID), view);
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultAsset = vaultSle->at(sfAsset);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -167,7 +168,7 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
// Let preclaim worry about the error for this
|
||||
return normalCost;
|
||||
}
|
||||
auto const vaultSle = view.read(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntryR const vaultSle(brokerSle->at(sfVaultID), view);
|
||||
if (!vaultSle)
|
||||
{
|
||||
// Let preclaim worry about the error for this
|
||||
@@ -273,7 +274,7 @@ LoanPay::preclaim(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const vaultID = loanBrokerSle->at(sfVaultID);
|
||||
auto const vaultSle = ctx.view.read(keylet::vault(vaultID));
|
||||
VaultEntryR const vaultSle(vaultID, ctx.view);
|
||||
if (!vaultSle)
|
||||
{
|
||||
// This should be impossible
|
||||
@@ -349,7 +350,7 @@ LoanPay::doApply()
|
||||
auto const brokerOwner = brokerSle->at(sfOwner);
|
||||
auto const brokerPseudoAccount = brokerSle->at(sfAccount);
|
||||
auto const vaultID = brokerSle->at(sfVaultID);
|
||||
auto const vaultSle = view.peek(keylet::vault(vaultID));
|
||||
VaultEntryW vaultSle(vaultID, view);
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultPseudoAccount = vaultSle->at(sfAccount);
|
||||
@@ -360,7 +361,7 @@ LoanPay::doApply()
|
||||
TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
|
||||
auto debtTotalProxy = brokerSle->at(sfDebtTotal);
|
||||
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale = vaultSle.assetsTotalScale();
|
||||
|
||||
// Send the broker fee to the owner if they have sufficient cover available,
|
||||
// _and_ if the owner can receive funds
|
||||
@@ -377,7 +378,7 @@ LoanPay::doApply()
|
||||
auto const minCover = [&]() {
|
||||
if (view.rules().enabled(fixCleanup3_2_0))
|
||||
{
|
||||
return minimumBrokerCover(debtTotalProxy.value(), coverRateMinimum, vaultSle);
|
||||
return vaultSle.minimumBrokerCover(debtTotalProxy.value(), coverRateMinimum);
|
||||
}
|
||||
// Round the minimum required cover up to be conservative. This ensures
|
||||
// CoverAvailable never drops below the theoretical minimum, protecting
|
||||
@@ -514,7 +515,7 @@ LoanPay::doApply()
|
||||
|
||||
//------------------------------------------------------
|
||||
// Vault object state changes
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
Number const assetsAvailableBefore = *assetsAvailableProxy;
|
||||
Number const assetsTotalBefore = *assetsTotalProxy;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -313,7 +314,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
return terNO_ACCOUNT;
|
||||
}
|
||||
|
||||
auto const vault = ctx.view.read(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntryR const vault(brokerSle->at(sfVaultID), ctx.view);
|
||||
if (!vault)
|
||||
{
|
||||
// Should be impossible
|
||||
@@ -322,7 +323,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (ctx.view.rules().enabled(featureLendingProtocolV1_1))
|
||||
{
|
||||
auto const phase = getVaultPhase(ctx.view, vault);
|
||||
auto const phase = vault.phase();
|
||||
if (phase == VaultPhase::Subscription)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Vault is still in the subscription phase.";
|
||||
@@ -351,7 +352,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
// already at AssetsMaximum cannot take another loan. Cash-basis origination
|
||||
// does not change AssetsTotal (see cash_basis::loanOriginationDeltas), so
|
||||
// this leftover instant-recognition gate must not apply there.
|
||||
if (getVaultVersion(vault) != VaultVersion::CashBasis && vault->at(sfAssetsMaximum) != 0 &&
|
||||
if (vault.version() != VaultVersion::CashBasis && vault->at(sfAssetsMaximum) != 0 &&
|
||||
vault->at(sfAssetsTotal) >= vault->at(sfAssetsMaximum))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Vault at maximum assets limit. Can't add another loan.";
|
||||
@@ -447,7 +448,7 @@ LoanSet::doApply()
|
||||
if (!brokerOwnerSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vaultSle = view.peek(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntryW vaultSle(brokerSle->at(sfVaultID), view);
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultPseudo = vaultSle->at(sfAccount);
|
||||
@@ -471,7 +472,7 @@ LoanSet::doApply()
|
||||
|
||||
auto vaultAvailableProxy = vaultSle->at(sfAssetsAvailable);
|
||||
auto vaultTotalProxy = vaultSle->at(sfAssetsTotal);
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale = vaultSle.assetsTotalScale();
|
||||
if (vaultAvailableProxy < principalRequested)
|
||||
{
|
||||
JLOG(j_.warn()) << "Insufficient assets available in the Vault to fund the loan.";
|
||||
@@ -499,8 +500,7 @@ LoanSet::doApply()
|
||||
properties.loanState.managementFeeDue);
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
*vaultSle->at(sfAssetsMaximum) == 0 ||
|
||||
getVaultVersion(vaultSle) == VaultVersion::CashBasis ||
|
||||
*vaultSle->at(sfAssetsMaximum) == 0 || vaultSle.version() == VaultVersion::CashBasis ||
|
||||
*vaultSle->at(sfAssetsMaximum) > *vaultTotalProxy,
|
||||
"xrpl::LoanSet::doApply",
|
||||
"instant-recognition vault is below maximum limit");
|
||||
@@ -565,7 +565,7 @@ LoanSet::doApply()
|
||||
auto const minCover = [&]() {
|
||||
if (ctx_.view().rules().enabled(fixCleanup3_2_0))
|
||||
{
|
||||
return minimumBrokerCover(newDebtTotal, coverRateMinimum, vaultSle);
|
||||
return vaultSle.minimumBrokerCover(newDebtTotal, coverRateMinimum);
|
||||
}
|
||||
|
||||
// Round the minimum required cover up to be conservative. This ensures
|
||||
@@ -702,7 +702,7 @@ LoanSet::doApply()
|
||||
*vaultAvailableProxy <= *vaultTotalProxy,
|
||||
"xrpl::LoanSet::doApply",
|
||||
"assets available must not be greater than assets outstanding");
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
// Update the balances in the loan broker
|
||||
adjustImpreciseNumber(brokerSle->at(sfDebtTotal), debtTotalDelta, vaultAsset, vaultScale);
|
||||
|
||||
@@ -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/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
@@ -61,7 +62,7 @@ VaultClawback::preflight(PreflightContext const& ctx)
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
clawbackAmount(
|
||||
SLE::ConstRef vault,
|
||||
VaultEntryR const& vault,
|
||||
std::optional<STAmount> const& maybeAmount,
|
||||
AccountID const& account)
|
||||
{
|
||||
@@ -78,7 +79,7 @@ clawbackAmount(
|
||||
TER
|
||||
VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntryR const vault(ctx.tx[sfVaultID], ctx.view);
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -232,7 +233,7 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
std::expected<std::pair<STAmount, STAmount>, TER>
|
||||
VaultClawback::assetsToClawback(
|
||||
SLE::Ref vault,
|
||||
VaultEntryW& vault,
|
||||
SLE::ConstRef sleShareIssuance,
|
||||
AccountID const& holder,
|
||||
STAmount const& clawbackAmount)
|
||||
@@ -362,7 +363,7 @@ VaultClawback::assetsToClawback(
|
||||
// sub-ULP trimmed off stays in the vault for the remaining shareholders.
|
||||
if (ctx_.view().rules().enabled(fixCleanup3_4_0) && assetsRecovered > beast::kZero)
|
||||
{
|
||||
auto const maybeClamped = clampToAssetsTotalScale(vault, -assetsRecovered);
|
||||
auto const maybeClamped = vault.clampToAssetsTotalScale(-assetsRecovered);
|
||||
if (!maybeClamped)
|
||||
return std::unexpected(maybeClamped.error());
|
||||
assetsRecovered = *maybeClamped;
|
||||
@@ -390,7 +391,7 @@ TER
|
||||
VaultClawback::doApply()
|
||||
{
|
||||
auto const& tx = ctx_.tx;
|
||||
auto const vault = view().peek(keylet::vault(tx[sfVaultID]));
|
||||
VaultEntryW vault(tx[sfVaultID], view());
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -475,7 +476,7 @@ VaultClawback::doApply()
|
||||
|
||||
assetsTotal -= assetsRecovered;
|
||||
assetsAvailable -= assetsRecovered;
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
// Transfer shares from holder to vault.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -185,9 +186,12 @@ VaultCreate::doApply()
|
||||
if (owner == nullptr)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto vault = std::make_shared<SLE>(keylet::vault(accountID_, sequence));
|
||||
VaultEntryW vault(accountID_, sequence, view());
|
||||
vault.newSLE();
|
||||
|
||||
if (auto ter = dirLink(view(), accountID_, vault))
|
||||
// dirLink takes a non-const SLE::pointer&, so pass it a copy of the handle.
|
||||
SLE::pointer vaultSle = vault.mutableRawSle();
|
||||
if (auto ter = dirLink(view(), accountID_, vaultSle))
|
||||
return ter;
|
||||
// We will create Vault and PseudoAccount, hence increase OwnerCount by 2
|
||||
increaseOwnerCount(view(), owner, {}, 2, j_);
|
||||
@@ -285,7 +289,7 @@ VaultCreate::doApply()
|
||||
vault->at(sfRedemptionDate) = tx[sfRedemptionDate];
|
||||
}
|
||||
}
|
||||
view().insert(vault);
|
||||
vault.insert();
|
||||
|
||||
// Explicitly create MPToken for the vault owner
|
||||
if (auto const err = authorizeMPToken(
|
||||
|
||||
@@ -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/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -43,7 +44,7 @@ VaultDelete::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
VaultDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntryR const vault(ctx.tx[sfVaultID], ctx.view);
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -96,7 +97,7 @@ VaultDelete::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
VaultDelete::doApply()
|
||||
{
|
||||
auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
|
||||
VaultEntryW vault(ctx_.tx[sfVaultID], view());
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -214,10 +215,10 @@ VaultDelete::doApply()
|
||||
}
|
||||
|
||||
// We are destroying Vault and PseudoAccount, hence decrease by 2
|
||||
decreaseOwnerCountForObject(view(), owner, vault, 2, j_);
|
||||
decreaseOwnerCountForObject(view(), owner, vault.mutableRawSle(), 2, j_);
|
||||
|
||||
// Destroy the vault.
|
||||
view().erase(vault);
|
||||
vault.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -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/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
@@ -33,7 +34,7 @@ namespace xrpl {
|
||||
|
||||
[[nodiscard]]
|
||||
static STAmount
|
||||
roundToVaultScale(STAmount const& amount, SLE::ConstRef vault)
|
||||
roundToVaultScale(STAmount const& amount, VaultEntryR const& vault)
|
||||
{
|
||||
XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::roundToVaultScale : valid vault sle");
|
||||
XRPL_ASSERT(
|
||||
@@ -103,13 +104,13 @@ VaultDeposit::preclaim(PreclaimContext const& ctx)
|
||||
auto const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0);
|
||||
auto const fix330Enabled = ctx.view.rules().enabled(fixCleanup3_3_0);
|
||||
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntryR const vault(ctx.tx[sfVaultID], ctx.view);
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
if (ctx.view.rules().enabled(featureLendingProtocolV1_1))
|
||||
{
|
||||
auto const phase = getVaultPhase(ctx.view, vault);
|
||||
auto const phase = vault.phase();
|
||||
if (phase == VaultPhase::Investment || phase == VaultPhase::Redemption)
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "VaultDeposit: vault deposit is not allowed in the investment "
|
||||
@@ -231,7 +232,7 @@ VaultDeposit::doApply()
|
||||
{
|
||||
bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0);
|
||||
bool const fix340Enabled = view().rules().enabled(fixCleanup3_4_0);
|
||||
auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
|
||||
VaultEntryW vault(ctx_.tx[sfVaultID], view());
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -350,7 +351,7 @@ VaultDeposit::doApply()
|
||||
// than the depositor paid. Keep the share count from the first round trip: the clamp
|
||||
// only drops a last digit of the new total. Converting the clamped amount back to
|
||||
// shares would mint fewer shares while still charging the N-share debit.
|
||||
auto const maybeClamped = clampToAssetsTotalScale(vault, assetsDeposited);
|
||||
auto const maybeClamped = vault.clampToAssetsTotalScale(assetsDeposited);
|
||||
if (!maybeClamped)
|
||||
return maybeClamped.error();
|
||||
assetsDeposited = *maybeClamped;
|
||||
@@ -380,7 +381,7 @@ VaultDeposit::doApply()
|
||||
|
||||
vault->at(sfAssetsTotal) += assetsDeposited;
|
||||
vault->at(sfAssetsAvailable) += assetsDeposited;
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
// A deposit must not push the vault over its limit.
|
||||
auto const maximum = *vault->at(sfAssetsMaximum);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -63,7 +64,7 @@ VaultSet::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
VaultSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntryR const vault(ctx.tx[sfVaultID], ctx.view);
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -123,7 +124,7 @@ VaultSet::doApply()
|
||||
auto const& tx = ctx_.tx;
|
||||
|
||||
// Update existing object.
|
||||
auto vault = view().peek(keylet::vault(tx[sfVaultID]));
|
||||
VaultEntryW vault(tx[sfVaultID], view());
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -170,7 +171,7 @@ VaultSet::doApply()
|
||||
// Note, we must update Vault object even if only DomainID is being updated
|
||||
// in Issuance object. Otherwise it's really difficult for Vault invariants
|
||||
// to verify the operation.
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
associateAsset(*vault, vaultAsset);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -83,13 +84,13 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
auto const fix330Enabled = ctx.view.rules().enabled(fixCleanup3_3_0);
|
||||
auto const fix340Enabled = ctx.view.rules().enabled(fixCleanup3_4_0);
|
||||
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntryR const vault(ctx.tx[sfVaultID], ctx.view);
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
if (ctx.view.rules().enabled(featureLendingProtocolV1_1))
|
||||
{
|
||||
if (getVaultPhase(ctx.view, vault) == VaultPhase::Investment)
|
||||
if (vault.phase() == VaultPhase::Investment)
|
||||
{
|
||||
JLOG(ctx.j.debug())
|
||||
<< "VaultWithdraw: vault withdrawal is not allowed in the investment phase.";
|
||||
@@ -278,7 +279,7 @@ TER
|
||||
VaultWithdraw::doApply()
|
||||
{
|
||||
bool const fix340Enabled = view().rules().enabled(fixCleanup3_4_0);
|
||||
auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
|
||||
VaultEntryW vault(ctx_.tx[sfVaultID], view());
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -394,7 +395,7 @@ VaultWithdraw::doApply()
|
||||
// backing value. Reject rather than burn shares for a zero payout. The fixed-assets branch
|
||||
// above has already rejected zero via the sharesRedeemed check.
|
||||
if (amount.asset() == share && assetsWithdrawn == beast::kZero &&
|
||||
assetsTotalForWithdrawal(vault, waiveUnrealizedLoss) != beast::kZero)
|
||||
vault.assetsTotalForWithdrawal(waiveUnrealizedLoss) != beast::kZero)
|
||||
{
|
||||
JLOG(j_.debug()) << "VaultWithdraw: fixed-share withdrawal rounds to zero assets";
|
||||
return tecPRECISION_LOSS;
|
||||
@@ -469,7 +470,7 @@ VaultWithdraw::doApply()
|
||||
// Round down at the posterior sfAssetsTotal scale so the payout never exceeds the
|
||||
// value represented by the redeemed shares. sharesRedeemed is intentionally not
|
||||
// re-derived: any trimmed residue stays with remaining shareholders.
|
||||
auto const maybeClamped = clampToAssetsTotalScale(vault, -assetsWithdrawn);
|
||||
auto const maybeClamped = vault.clampToAssetsTotalScale(-assetsWithdrawn);
|
||||
if (!maybeClamped)
|
||||
return maybeClamped.error(); // LCOV_EXCL_LINE
|
||||
assetsWithdrawn = *maybeClamped;
|
||||
@@ -543,7 +544,7 @@ VaultWithdraw::doApply()
|
||||
assetsTotal -= assetsWithdrawn;
|
||||
assetsAvailable -= assetsWithdrawn;
|
||||
}
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -1682,7 +1683,8 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
"loanOriginationDeltas dispatcher: amendment enabled, legacy vault picks "
|
||||
"InstantRecognition");
|
||||
Env const env{*this};
|
||||
auto const deltas = loanOriginationDeltas(legacyVault, principalRequested, interestDue);
|
||||
auto const deltas = loanOriginationDeltas(
|
||||
VaultEntryR(legacyVault, *env.current()), principalRequested, interestDue);
|
||||
auto const expected =
|
||||
xrpl::instant_recognition::loanOriginationDeltas(principalRequested, interestDue);
|
||||
BEAST_EXPECT(deltas.assetsTotalDelta == expected.assetsTotalDelta);
|
||||
@@ -1694,8 +1696,8 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
"loanOriginationDeltas dispatcher: amendment enabled, LEVersion == "
|
||||
"VaultVersion::CashBasis picks CashBasis");
|
||||
Env const env{*this};
|
||||
auto const deltas =
|
||||
loanOriginationDeltas(cashBasisVault, principalRequested, interestDue);
|
||||
auto const deltas = loanOriginationDeltas(
|
||||
VaultEntryR(cashBasisVault, *env.current()), principalRequested, interestDue);
|
||||
auto const expected = xrpl::cash_basis::loanOriginationDeltas(principalRequested);
|
||||
BEAST_EXPECT(deltas.assetsTotalDelta == expected.assetsTotalDelta);
|
||||
BEAST_EXPECT(deltas.debtTotalDelta == expected.debtTotalDelta);
|
||||
@@ -1721,7 +1723,8 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
"picks InstantRecognition");
|
||||
Env const env{*this};
|
||||
BEAST_EXPECT(
|
||||
loanOriginationExceedsVaultMaximum(legacyVault, vaultTotal, interestDue) ==
|
||||
loanOriginationExceedsVaultMaximum(
|
||||
VaultEntryR(legacyVault, *env.current()), vaultTotal, interestDue) ==
|
||||
xrpl::instant_recognition::loanOriginationExceedsVaultMaximum(
|
||||
vaultMaximum, vaultTotal, interestDue));
|
||||
}
|
||||
@@ -1732,8 +1735,8 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
"VaultVersion::CashBasis picks CashBasis");
|
||||
Env const env{*this};
|
||||
BEAST_EXPECT(
|
||||
loanOriginationExceedsVaultMaximum(cashBasisVault, vaultTotal, interestDue) ==
|
||||
false);
|
||||
loanOriginationExceedsVaultMaximum(
|
||||
VaultEntryR(cashBasisVault, *env.current()), vaultTotal, interestDue) == false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1752,7 +1755,7 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
Env const env{*this};
|
||||
auto sle = makeLoanSle(Number{1'000}, Number{800}, Number{50});
|
||||
BEAST_EXPECT(
|
||||
loanVaultExposure(legacyVault, sle) ==
|
||||
loanVaultExposure(VaultEntryR(legacyVault, *env.current()), sle) ==
|
||||
xrpl::instant_recognition::loanVaultExposure(sle));
|
||||
}
|
||||
|
||||
@@ -1764,7 +1767,8 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
Env const env{*this};
|
||||
auto sle = makeLoanSle(Number{1'000}, Number{800}, Number{50});
|
||||
BEAST_EXPECT(
|
||||
loanVaultExposure(cashBasisVault, sle) == xrpl::cash_basis::loanVaultExposure(sle));
|
||||
loanVaultExposure(VaultEntryR(cashBasisVault, *env.current()), sle) ==
|
||||
xrpl::cash_basis::loanVaultExposure(sle));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1787,7 +1791,7 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
"loanPaymentDeltas dispatcher: amendment enabled, legacy vault picks "
|
||||
"InstantRecognition");
|
||||
Env const env{*this};
|
||||
auto const deltas = loanPaymentDeltas(legacyVault, parts);
|
||||
auto const deltas = loanPaymentDeltas(VaultEntryR(legacyVault, *env.current()), parts);
|
||||
auto const expected = xrpl::instant_recognition::loanPaymentDeltas(parts);
|
||||
BEAST_EXPECT(deltas.assetsTotalDelta == expected.assetsTotalDelta);
|
||||
BEAST_EXPECT(deltas.debtTotalDelta == expected.debtTotalDelta);
|
||||
@@ -1799,7 +1803,8 @@ class LendingHelpers_test : public beast::unit_test::Suite
|
||||
"VaultVersion::CashBasis "
|
||||
"picks CashBasis");
|
||||
Env const env{*this};
|
||||
auto const deltas = loanPaymentDeltas(cashBasisVault, parts);
|
||||
auto const deltas =
|
||||
loanPaymentDeltas(VaultEntryR(cashBasisVault, *env.current()), parts);
|
||||
auto const expected = xrpl::cash_basis::loanPaymentDeltas(parts);
|
||||
BEAST_EXPECT(deltas.assetsTotalDelta == expected.assetsTotalDelta);
|
||||
BEAST_EXPECT(deltas.debtTotalDelta == expected.debtTotalDelta);
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -1126,7 +1126,7 @@ private:
|
||||
auto const vaultSle = env.le(broker.vaultKeylet());
|
||||
BEAST_EXPECT(vaultSle);
|
||||
BEAST_EXPECT(!vaultSle->isFieldPresent(sfLEVersion));
|
||||
BEAST_EXPECT(getVaultVersion(vaultSle) == VaultVersion::Legacy);
|
||||
BEAST_EXPECT(VaultEntryR(vaultSle, *env.current()).version() == VaultVersion::Legacy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -665,7 +666,7 @@ private:
|
||||
return std::nullopt;
|
||||
if (!BEAST_EXPECT(tinyLoanSle->at(sfLoanScale) == -12) ||
|
||||
!BEAST_EXPECT(bigLoanSle->at(sfLoanScale) == -11) ||
|
||||
!BEAST_EXPECT(getAssetsTotalScale(vaultSle) == -11))
|
||||
!BEAST_EXPECT(VaultEntryR(vaultSle, *env.current()).assetsTotalScale() == -11))
|
||||
return std::nullopt;
|
||||
|
||||
// Use issuer clawback to reduce cover to the minimum the
|
||||
@@ -792,7 +793,7 @@ private:
|
||||
|
||||
auto const coverAvail = brokerSle->at(sfCoverAvailable);
|
||||
auto const debtTotal = brokerSle->at(sfDebtTotal);
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale = VaultEntryR(vaultSle, *env.current()).assetsTotalScale();
|
||||
auto const debtScale = scale(debtTotal, asset);
|
||||
|
||||
// Sanity: debt scale differs from vault scale for this setup.
|
||||
@@ -805,8 +806,9 @@ private:
|
||||
tenthBipsOfValue(debtTotal, TenthBips32{c.brokerParams.coverRateMin}),
|
||||
debtScale);
|
||||
}();
|
||||
auto const newMin = minimumBrokerCover(
|
||||
debtTotal, TenthBips32{c.brokerParams.coverRateMin}, vaultSle);
|
||||
auto const newMin =
|
||||
VaultEntryR(vaultSle, *env.current())
|
||||
.minimumBrokerCover(debtTotal, TenthBips32{c.brokerParams.coverRateMin});
|
||||
|
||||
// The new (vaultScale) minimum must be strictly larger than
|
||||
// the old (debtScale) minimum — that is the gap the amendment
|
||||
@@ -890,7 +892,7 @@ private:
|
||||
auto const vaultSle = env.le(keylet::vault(c.broker.vaultID));
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale = VaultEntryR(vaultSle, *env.current()).assetsTotalScale();
|
||||
BEAST_EXPECT(vaultScale == -11);
|
||||
|
||||
// Now try to create a tiny additional loan. Principal is
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -190,7 +190,7 @@ protected:
|
||||
using namespace jtx;
|
||||
|
||||
auto const vaultSle = env.le(keylet::vault(vaultID));
|
||||
return getAssetsTotalScale(vaultSle);
|
||||
return VaultEntryR(vaultSle, *env.current()).assetsTotalScale();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -346,7 +346,8 @@ protected:
|
||||
{
|
||||
auto const expectedDebt =
|
||||
env.current()->rules().enabled(featureLendingProtocolV1_1) &&
|
||||
getVaultVersion(vaultSle) == VaultVersion::CashBasis
|
||||
VaultEntryR(vaultSle, *env.current()).version() ==
|
||||
VaultVersion::CashBasis
|
||||
? principalOutstanding
|
||||
: principalOutstanding + interestOwed;
|
||||
env.test.BEAST_EXPECT(brokerDebt == expectedDebt);
|
||||
@@ -451,7 +452,8 @@ protected:
|
||||
env.test.BEAST_EXPECT(
|
||||
vaultSle->at(sfLossUnrealized) ==
|
||||
(env.current()->rules().enabled(featureLendingProtocolV1_1) &&
|
||||
getVaultVersion(vaultSle) == VaultVersion::CashBasis
|
||||
VaultEntryR(vaultSle, *env.current()).version() ==
|
||||
VaultVersion::CashBasis
|
||||
? principalOutstanding
|
||||
: totalValue - managementFeeOutstanding));
|
||||
}
|
||||
@@ -666,7 +668,8 @@ protected:
|
||||
vaultSle->at(sfAssetsTotal) - vaultSle->at(sfAssetsAvailable);
|
||||
auto const unrealizedLoss = vaultSle->at(sfLossUnrealized) +
|
||||
(env.current()->rules().enabled(featureLendingProtocolV1_1) &&
|
||||
getVaultVersion(vaultSle) == VaultVersion::CashBasis
|
||||
VaultEntryR(vaultSle, *env.current()).version() ==
|
||||
VaultVersion::CashBasis
|
||||
? state.principalOutstanding
|
||||
: state.totalValue - state.managementFeeOutstanding);
|
||||
|
||||
|
||||
@@ -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/VaultEntry.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -1521,7 +1522,7 @@ private:
|
||||
// same conversion helper VaultClawback itself uses, rather than
|
||||
// assuming an exact 90/10 split holds under truncation.
|
||||
auto const maybeSharesDestroyed = assetsToSharesWithdraw(
|
||||
vaultBefore,
|
||||
VaultEntryR(vaultBefore, *env.current()),
|
||||
issuanceBefore,
|
||||
setup.usd(9'000).value(),
|
||||
TruncateShares::Yes,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -473,7 +473,7 @@ private:
|
||||
auto const sle = env.le(keylet);
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(getVaultPhase(*env.current(), sle) == VaultPhase::NoPhase);
|
||||
BEAST_EXPECT(VaultEntryR(sle, *env.current()).phase() == VaultPhase::NoPhase);
|
||||
};
|
||||
|
||||
// Advance the clock through a wide range of ledger times: an open-ended vault's phase
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/ledger/entries/VaultEntry.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -29,8 +30,9 @@ namespace xrpl {
|
||||
|
||||
// True unit test of `clampToAssetsTotalScale`. The function under test only
|
||||
// reads sfAsset and sfAssetsTotal from the vault SLE and never touches a
|
||||
// ledger view or Rules, so a bare in-memory ltVAULT SLE is enough; there is
|
||||
// no jtx::Env and no transaction submitted anywhere in this file.
|
||||
// ledger view or Rules, so a bare in-memory ltVAULT SLE is enough. The only
|
||||
// jtx::Env is the one runCases() needs so VaultEntryR can wrap that SLE; no
|
||||
// transaction is submitted anywhere in this file.
|
||||
//
|
||||
// Number regime: this suite relies on the default thread_local Number
|
||||
// mantissa range, which src/libxrpl/basics/Number.cpp initializes to
|
||||
@@ -86,6 +88,9 @@ private:
|
||||
Number::RoundingMode::Upward,
|
||||
Number::RoundingMode::TowardsZero};
|
||||
|
||||
test::jtx::Env const env{*this};
|
||||
auto const view = env.current();
|
||||
|
||||
for (auto const& c : cases)
|
||||
{
|
||||
testcase(c.name);
|
||||
@@ -106,7 +111,7 @@ private:
|
||||
for (auto const mode : modes)
|
||||
{
|
||||
NumberRoundModeGuard const rg(mode);
|
||||
auto const result = clampToAssetsTotalScale(vault, delta);
|
||||
auto const result = VaultEntryR(vault, *view).clampToAssetsTotalScale(delta);
|
||||
|
||||
// The function must be insensitive to the caller's ambient
|
||||
// rounding mode: every mode must agree with the first one
|
||||
|
||||
@@ -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