mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-30 16:58:30 +00:00
featureVaultContinuousAccrual
This commit is contained in:
@@ -101,6 +101,24 @@ static constexpr std::uint32_t kSecondsInYear = 365 * 24 * 60 * 60;
|
||||
Number
|
||||
loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
|
||||
|
||||
/**
|
||||
* Assets a loan earns per second at this principal outstanding.
|
||||
*
|
||||
* Equation (27) of XLS-66 is linear in elapsed time, and sfPaymentInterval
|
||||
* cancels out of it, so a loan's accrual rate depends only on its principal
|
||||
* and interest rate. Principal is flat between payments, which makes the rate
|
||||
* piecewise-constant with breakpoints exactly at the events that update it —
|
||||
* summing it across a vault's loans is therefore exact, not an approximation.
|
||||
*/
|
||||
inline Number
|
||||
loanAccrualRate(Number const& principalOutstanding, TenthBips32 interestRate)
|
||||
{
|
||||
if (interestRate == TenthBips32{0} || principalOutstanding <= Number{})
|
||||
return Number{};
|
||||
|
||||
return tenthBipsOfValue(principalOutstanding, interestRate) / Number{kSecondsInYear};
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the periodic payment is always rounded consistently
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -16,20 +17,111 @@
|
||||
namespace xrpl {
|
||||
|
||||
class STTx;
|
||||
/**
|
||||
* Interest the vault's loans have earned since sfLastAccrualTime, capped by
|
||||
* the sfUnearnedInterest budget still left to recognize.
|
||||
*
|
||||
* sfAssetsTotal is only credited when a loan event settles the vault, so
|
||||
* between those events it lags by this amount. Adding it back at read time
|
||||
* recognizes interest continuously as it is earned, without writing to
|
||||
* sfAssetsTotal outside the loan transactions.
|
||||
*
|
||||
* Returns zero when nothing is accruing — no rate, no budget left, or a vault
|
||||
* created before featureLendingProtocolV1_1.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
vaultAccruedInterest(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* Whether a rolling vault is inside a dealing window at this close time.
|
||||
*
|
||||
* A rolling vault deals in [SubscriptionDate + k * DealingInterval,
|
||||
* SubscriptionDate + k * DealingInterval + DealingWindow) for integer k >= 0.
|
||||
* Returns true for any vault that is not rolling, which has no windows to be
|
||||
* outside of, and false before the first window opens.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
inDealingWindow(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* End of the dealing window containing this close time.
|
||||
*
|
||||
* Only meaningful when inDealingWindow is true for a rolling vault; it is the
|
||||
* sfStruckUntil written when a window's price is struck.
|
||||
*/
|
||||
[[nodiscard]] std::uint32_t
|
||||
dealingWindowEnd(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* The price every deal in the current window converts at, in vault asset per
|
||||
* share, or nullopt when no struck price governs this ledger.
|
||||
*
|
||||
* Returns a price only for a rolling vault inside a window whose sfStruckUntil
|
||||
* matches that window's end. Accrual continues underneath it: the dealing price
|
||||
* is frozen for the window, the accounting is not.
|
||||
*/
|
||||
/**
|
||||
* The interest recognition method of a vault.
|
||||
*
|
||||
* Returns sfAccountingMethod where it is present. A vault created before
|
||||
* featureVaultContinuousAccrual carries no such field, so the method is derived
|
||||
* from its schema version instead: CashBasis recognizes interest as it is
|
||||
* collected, and anything older is Legacy, which recognizes a loan's whole-life
|
||||
* interest at origination. Every vault that exists today therefore resolves
|
||||
* without being touched.
|
||||
*/
|
||||
[[nodiscard]] std::uint8_t
|
||||
getAccountingMethod(SLE::const_ref vault);
|
||||
|
||||
[[nodiscard]] std::optional<Number>
|
||||
struckPriceInForce(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* Strike the price for the current window if it has not been struck yet.
|
||||
*
|
||||
* Called by the first deposit or withdrawal of a window. Does nothing for a
|
||||
* vault that is not rolling, outside a window, or where this window's price is
|
||||
* already struck, so it is safe to call unconditionally.
|
||||
*/
|
||||
void
|
||||
strikeWindowPrice(ApplyView& view, SLE::ref vault, SLE::const_ref issuance);
|
||||
|
||||
/**
|
||||
* Credit interest earned since the last settlement into sfAssetsTotal, draw
|
||||
* it out of the sfUnearnedInterest budget, and stamp the current close time.
|
||||
*
|
||||
* Must be called before adjusting sfAccrualRate, so the elapsed period is
|
||||
* charged at the rate that was in effect over it. Only the ttLOAN_*
|
||||
* transactions may call this: ValidVault requires sfAssetsTotal to move with
|
||||
* the vault balance on deposit and withdraw, which settling would violate.
|
||||
* Pricing does not need it — the conversion helpers add elapsed interest
|
||||
* themselves.
|
||||
*/
|
||||
void
|
||||
accrueVault(ApplyView& view, SLE::ref vault);
|
||||
|
||||
/**
|
||||
* From the perspective of a vault, return the number of shares to give
|
||||
* depositor when they offer a fixed amount of assets. Note, since shares are
|
||||
* MPT, this number is integral and always truncated in this calculation.
|
||||
*
|
||||
* @param vault The vault SLE.
|
||||
* @param issuance The MPTokenIssuance SLE for the vault's shares.
|
||||
* @param assets The amount of assets to convert.
|
||||
*
|
||||
* @return The number of shares, or nullopt on error.
|
||||
* /**
|
||||
* * From the perspective of a vault, return the number of shares to give
|
||||
* * depositor when they offer a fixed amount of assets. Note, since shares are
|
||||
* * MPT, this number is integral and always truncated in this calculation.
|
||||
* *
|
||||
* * @param vault The vault SLE.
|
||||
* * @param issuance The MPTokenIssuance SLE for the vault's shares.
|
||||
* * @param assets The amount of assets to convert.
|
||||
* *
|
||||
* * @return The number of shares, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets);
|
||||
assetsToSharesDeposit(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& assets);
|
||||
|
||||
/**
|
||||
* From the perspective of a vault, return the number of assets to take from
|
||||
@@ -43,7 +135,11 @@ assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount co
|
||||
* @return The number of assets, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares);
|
||||
sharesToAssetsDeposit(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& shares);
|
||||
|
||||
/**
|
||||
* Adjusts a requested asset change (`delta`) to match the decimal scale of the
|
||||
@@ -91,11 +187,12 @@ enum class WaiveUnrealizedLoss : bool { No = false, Yes = true };
|
||||
* unrealized loss is waived. Used by assetsToSharesWithdraw and
|
||||
* sharesToAssetsWithdraw as the numerator of the share/asset exchange rate.
|
||||
*
|
||||
* @param view The ledger view, for interest accrued since the last settlement.
|
||||
* @param vault The vault SLE.
|
||||
* @param waive Whether to skip subtracting the unrealized loss.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
assetsTotalForWithdrawal(SLE::const_ref vault, WaiveUnrealizedLoss waive);
|
||||
assetsTotalForWithdrawal(ReadView const& view, SLE::const_ref vault, WaiveUnrealizedLoss waive);
|
||||
|
||||
/**
|
||||
* Returns true if debiting `amount` from `total` (the current value of a
|
||||
@@ -131,6 +228,7 @@ debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& assets,
|
||||
@@ -152,6 +250,7 @@ assetsToSharesWithdraw(
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& shares,
|
||||
|
||||
@@ -307,6 +307,19 @@ constexpr std::size_t kMaxDataPayloadLength = 256;
|
||||
*/
|
||||
constexpr std::uint8_t kVaultStrategyFirstComeFirstServe = 1;
|
||||
|
||||
/**
|
||||
* Vault interest recognition methods.
|
||||
*
|
||||
* Legacy recognizes a loan's whole-life interest at origination and is the
|
||||
* implicit method for vaults created before featureLendingProtocolV1_1. Cash
|
||||
* recognizes interest as it is collected; accrual recognizes it continuously
|
||||
* as it is earned. Fixed at VaultCreate — changing it would reprice every
|
||||
* outstanding share in a single step.
|
||||
*/
|
||||
constexpr std::uint8_t kVaultAccountingLegacy = 0;
|
||||
constexpr std::uint8_t kVaultAccountingCash = 1;
|
||||
constexpr std::uint8_t kVaultAccountingAccrual = 2;
|
||||
|
||||
/**
|
||||
* Default IOU scale factor for a Vault
|
||||
*/
|
||||
@@ -316,7 +329,13 @@ constexpr std::uint8_t kVaultDefaultIouScale = 6;
|
||||
* 1 IOU can be always converted to shares.
|
||||
* 10^19 > maxMPTokenAmount (2^64-1) > 10^18
|
||||
*/
|
||||
constexpr std::uint8_t kVaultMaximumIouScale = 18;
|
||||
constexpr std::uint8_t kVaultMaximumIouScale =
|
||||
18; /** Largest deposit or redemption fee a vault may charge, in 1/10 bips.
|
||||
|
||||
Matches kMaxTransferFee: half of what is moved is the most any fee may
|
||||
retain.
|
||||
*/
|
||||
constexpr std::uint32_t kMaxVaultFee = 50'000;
|
||||
|
||||
/**
|
||||
* Vault ledger-entry schema versions. Assigned to newly created
|
||||
@@ -330,12 +349,16 @@ enum class VaultVersion : uint8_t {
|
||||
};
|
||||
|
||||
/**
|
||||
* Vault kind. Distinguishes closed-ended vaults from the default open-ended
|
||||
* kind. Persisted as sfVaultKind (UINT8); absent means OpenEnded.
|
||||
* Vault kind. Distinguishes closed-ended and rolling vaults from the default
|
||||
* open-ended kind. Persisted as sfVaultKind (UINT8); absent means OpenEnded.
|
||||
*
|
||||
* A rolling vault deals in a window that reopens every sfDealingInterval
|
||||
* seconds and stays open for sfDealingWindow of them.
|
||||
*/
|
||||
enum class VaultKind : std::uint8_t {
|
||||
OpenEnded = 0,
|
||||
ClosedEnded = 1,
|
||||
Rolling = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FEATURE(VaultContinuousAccrual, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocolV1_2, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -437,6 +437,7 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
|
||||
{sfIssuerEncryptedBalance, SoeOptional},
|
||||
{sfAuditorEncryptedBalance, SoeOptional},
|
||||
{sfHolderEncryptionKey, SoeOptional},
|
||||
{sfRedemptionAfter, SoeDefault},
|
||||
}))
|
||||
|
||||
/** A ledger object which tracks Oracle
|
||||
@@ -511,13 +512,24 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
|
||||
{sfAssetsAvailable, SoeDefault},
|
||||
{sfAssetsMaximum, SoeDefault},
|
||||
{sfLossUnrealized, SoeDefault},
|
||||
{sfUnearnedInterest, SoeDefault},
|
||||
{sfAccrualRate, SoeDefault},
|
||||
{sfLastAccrualTime, SoeDefault},
|
||||
{sfShareMPTID, SoeRequired},
|
||||
{sfWithdrawalPolicy, SoeRequired},
|
||||
{sfAccountingMethod, SoeDefault},
|
||||
{sfScale, SoeDefault},
|
||||
{sfLEVersion, SoeDefault},
|
||||
{sfVaultKind, SoeDefault},
|
||||
{sfSubscriptionDate, SoeOptional},
|
||||
{sfRedemptionDate, SoeOptional},
|
||||
{sfDealingInterval, SoeDefault},
|
||||
{sfDealingWindow, SoeDefault},
|
||||
{sfStruckPrice, SoeDefault},
|
||||
{sfStruckUntil, SoeDefault},
|
||||
{sfDepositFee, SoeDefault},
|
||||
{sfRedemptionFee, SoeDefault},
|
||||
{sfRedemptionPeriod, SoeDefault},
|
||||
// no SharesTotal ever (use MPTIssuance.sfOutstandingAmount)
|
||||
// no PermissionedDomainID ever (use MPTIssuance.sfDomainID)
|
||||
}))
|
||||
|
||||
@@ -28,6 +28,7 @@ TYPED_SFIELD(sfWasLockingChainSend, UINT8, 19)
|
||||
TYPED_SFIELD(sfWithdrawalPolicy, UINT8, 20)
|
||||
TYPED_SFIELD(sfContractResult, UINT8, 21)
|
||||
TYPED_SFIELD(sfVaultKind, UINT8, 22)
|
||||
TYPED_SFIELD(sfAccountingMethod, UINT8, 23)
|
||||
|
||||
// 16-bit integers (common)
|
||||
TYPED_SFIELD(sfLedgerEntryType, UINT16, 1, SField::kSmdNever)
|
||||
@@ -128,6 +129,14 @@ TYPED_SFIELD(sfBytecodeSizeLimit, UINT32, 82)
|
||||
TYPED_SFIELD(sfGasPrice, UINT32, 83)
|
||||
TYPED_SFIELD(sfGas, UINT32, 84)
|
||||
TYPED_SFIELD(sfGasUsed, UINT32, 85)
|
||||
TYPED_SFIELD(sfLastAccrualTime, UINT32, 86)
|
||||
TYPED_SFIELD(sfDealingInterval, UINT32, 87)
|
||||
TYPED_SFIELD(sfDealingWindow, UINT32, 88)
|
||||
TYPED_SFIELD(sfStruckUntil, UINT32, 89)
|
||||
TYPED_SFIELD(sfDepositFee, UINT32, 90)
|
||||
TYPED_SFIELD(sfRedemptionFee, UINT32, 91)
|
||||
TYPED_SFIELD(sfRedemptionPeriod, UINT32, 92)
|
||||
TYPED_SFIELD(sfRedemptionAfter, UINT32, 93)
|
||||
|
||||
// 64-bit integers (common)
|
||||
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
||||
@@ -239,6 +248,11 @@ TYPED_SFIELD(sfPrincipalRequested, NUMBER, 14)
|
||||
TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15, SField::kSmdNeedsAsset | SField::kSmdDefault)
|
||||
TYPED_SFIELD(sfPeriodicPayment, NUMBER, 16)
|
||||
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17, SField::kSmdNeedsAsset | SField::kSmdDefault)
|
||||
TYPED_SFIELD(sfUnearnedInterest, NUMBER, 18, SField::kSmdNeedsAsset | SField::kSmdDefault)
|
||||
// Assets earned per second, summed over a vault's performing loans. A rate,
|
||||
// not an amount, so it carries no asset association.
|
||||
TYPED_SFIELD(sfAccrualRate, NUMBER, 19, SField::kSmdDefault)
|
||||
TYPED_SFIELD(sfStruckPrice, NUMBER, 20, SField::kSmdDefault)
|
||||
|
||||
// 32-bit signed (common)
|
||||
TYPED_SFIELD(sfLoanScale, INT32, 1)
|
||||
|
||||
@@ -788,6 +788,12 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
{sfVaultKind, SoeOptional},
|
||||
{sfSubscriptionDate, SoeOptional},
|
||||
{sfRedemptionDate, SoeOptional},
|
||||
{sfDealingInterval, SoeOptional},
|
||||
{sfDealingWindow, SoeOptional},
|
||||
{sfDepositFee, SoeOptional},
|
||||
{sfRedemptionFee, SoeOptional},
|
||||
{sfRedemptionPeriod, SoeOptional},
|
||||
{sfAccountingMethod, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This transaction updates a single asset vault. */
|
||||
@@ -804,6 +810,9 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
{sfAssetsMaximum, SoeOptional},
|
||||
{sfDomainID, SoeOptional},
|
||||
{sfData, SoeOptional},
|
||||
{sfDepositFee, SoeOptional},
|
||||
{sfRedemptionFee, SoeOptional},
|
||||
{sfRedemptionPeriod, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This transaction deletes a single asset vault. */
|
||||
|
||||
@@ -242,6 +242,78 @@ public:
|
||||
return this->sle_->isFieldPresent(sfLossUnrealized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfUnearnedInterest (SoeDefault)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_NUMBER::type::value_type>
|
||||
getUnearnedInterest() const
|
||||
{
|
||||
if (hasUnearnedInterest())
|
||||
return this->sle_->at(sfUnearnedInterest);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfUnearnedInterest is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasUnearnedInterest() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfUnearnedInterest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAccrualRate (SoeDefault)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_NUMBER::type::value_type>
|
||||
getAccrualRate() const
|
||||
{
|
||||
if (hasAccrualRate())
|
||||
return this->sle_->at(sfAccrualRate);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAccrualRate is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAccrualRate() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfAccrualRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfLastAccrualTime (SoeDefault)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getLastAccrualTime() const
|
||||
{
|
||||
if (hasLastAccrualTime())
|
||||
return this->sle_->at(sfLastAccrualTime);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfLastAccrualTime is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasLastAccrualTime() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfLastAccrualTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfShareMPTID (SoeRequired)
|
||||
* @return The field value.
|
||||
@@ -264,6 +336,30 @@ public:
|
||||
return this->sle_->at(sfWithdrawalPolicy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAccountingMethod (SoeDefault)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT8::type::value_type>
|
||||
getAccountingMethod() const
|
||||
{
|
||||
if (hasAccountingMethod())
|
||||
return this->sle_->at(sfAccountingMethod);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAccountingMethod is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAccountingMethod() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfAccountingMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfScale (SoeDefault)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -571,6 +667,39 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfUnearnedInterest (SoeDefault)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
VaultBuilder&
|
||||
setUnearnedInterest(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
|
||||
{
|
||||
object_[sfUnearnedInterest] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAccrualRate (SoeDefault)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
VaultBuilder&
|
||||
setAccrualRate(std::decay_t<typename SF_NUMBER::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAccrualRate] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfLastAccrualTime (SoeDefault)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
VaultBuilder&
|
||||
setLastAccrualTime(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfLastAccrualTime] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfShareMPTID (SoeRequired)
|
||||
* @return Reference to this builder for method chaining.
|
||||
@@ -593,6 +722,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAccountingMethod (SoeDefault)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
VaultBuilder&
|
||||
setAccountingMethod(std::decay_t<typename SF_UINT8::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAccountingMethod] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfScale (SoeDefault)
|
||||
* @return Reference to this builder for method chaining.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <xrpl/tx/invariants/PermissionedDEXInvariant.h>
|
||||
#include <xrpl/tx/invariants/PermissionedDomainInvariant.h>
|
||||
#include <xrpl/tx/invariants/SponsorshipInvariant.h>
|
||||
#include <xrpl/tx/invariants/VaultAccrualInvariant.h>
|
||||
#include <xrpl/tx/invariants/VaultInvariant.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -457,6 +458,7 @@ using InvariantChecks = std::tuple<
|
||||
ValidLoanBroker,
|
||||
ValidLoan,
|
||||
ValidVault,
|
||||
ValidVaultAccrual,
|
||||
ValidConfidentialMPToken,
|
||||
ValidMPTBalanceChanges,
|
||||
ValidAmounts,
|
||||
|
||||
57
include/xrpl/tx/invariants/VaultAccrualInvariant.h
Normal file
57
include/xrpl/tx/invariants/VaultAccrualInvariant.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Invariant: the continuous-accrual state of a vault moves only where it
|
||||
* is allowed to.
|
||||
*
|
||||
* Enforces XLS Vault Continuous Accrual section 7 for every Vault entry the
|
||||
* transaction touches:
|
||||
*
|
||||
* 1. sfAccrualRate and sfUnearnedInterest are never negative.
|
||||
* 2. sfLastAccrualTime never decreases, and changes only in a ttLOAN_*
|
||||
* transaction, which is where the vault settles.
|
||||
* 4. VaultDeposit, VaultWithdraw and VaultClawback leave sfAccrualRate,
|
||||
* sfUnearnedInterest and sfLastAccrualTime untouched: they price against the
|
||||
* accrued value but never settle it.
|
||||
* 6. sfStruckPrice changes at most once per window and only in a dealing
|
||||
* transaction, and sfStruckUntil only ever moves forward.
|
||||
*
|
||||
* Numbers 3 and 5 of that section need the loan book and the fee split
|
||||
* respectively, and are not enforced here.
|
||||
*/
|
||||
class ValidVaultAccrual
|
||||
{
|
||||
struct Accrual final
|
||||
{
|
||||
Number accrualRate = Number{};
|
||||
Number unearnedInterest = Number{};
|
||||
std::uint32_t lastAccrualTime = 0;
|
||||
Number struckPrice = Number{};
|
||||
std::uint32_t struckUntil = 0;
|
||||
};
|
||||
|
||||
std::vector<Accrual> before_;
|
||||
std::vector<Accrual> after_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, SLE::const_ref, SLE::const_ref);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
Reference in New Issue
Block a user