mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 19:45:53 +00:00
[WIP] Define remaining transactions, start implementing LoanBrokerSet
- Does not build - Transactions: LoanDelete, LoanManage, LoanDraw, LoanPay - LoanBrokerSet creation mostly done. Need update. - Also added a lookup table for pseudo account fields.
This commit is contained in:
@@ -82,6 +82,26 @@ std::size_t constexpr maxDeletableTokenOfferEntries = 500;
|
||||
*/
|
||||
std::uint16_t constexpr maxTransferFee = 50000;
|
||||
|
||||
/** The maximum management fee rate allowed in lending.
|
||||
|
||||
TODO: Is this a good name?
|
||||
|
||||
Valid values for the the management fee charged by the Lending Protocol are
|
||||
between 0 and 10000 inclusive. A value of 1 is equivalent to 1/10 basis
|
||||
point fee or 0.001%.
|
||||
*/
|
||||
std::uint16_t constexpr maxFeeRate = 10'000;
|
||||
|
||||
/** The maximum coverage rate allowed in lending.
|
||||
|
||||
TODO: Is this a good name?
|
||||
|
||||
Valid values for the coverage rate charged by the Lending Protocol for first
|
||||
loss capital operations are between 0 and 100000 inclusive. A value of 1 is
|
||||
equivalent to 1/10 bps or 0.001%.
|
||||
*/
|
||||
std::uint16_t constexpr maxCoverRate = 100'000;
|
||||
|
||||
/** The maximum length of a URI inside an NFT */
|
||||
std::size_t constexpr maxTokenURILength = 256;
|
||||
|
||||
|
||||
@@ -237,6 +237,13 @@ constexpr std::uint32_t const tfVaultCreateMask = ~(tfUniversal | tfVaultPrivate
|
||||
// True, indicates the load supports overpayments
|
||||
constexpr std::uint32_t const tfLoanOverpayment = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal | tfLoanOverpayment);
|
||||
|
||||
// LoanManage flags:
|
||||
constexpr std::uint32_t const tfLoanDefault = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanImpair = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanUnimpair = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair);
|
||||
|
||||
// clang-format on
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -501,12 +501,12 @@ LEDGER_ENTRY(ltLOAN_BROKER, 0x0084, LoanBroker, loan_broker, ({
|
||||
{sfOwner, soeREQUIRED},
|
||||
{sfData, soeDEFAULT},
|
||||
{sfManagementFeeRate, soeDEFAULT},
|
||||
{sfOwnerCount, soeREQUIRED},
|
||||
{sfDebtTotal, soeREQUIRED},
|
||||
{sfDebtMaximum, soeREQUIRED},
|
||||
{sfCoverAvailable, soeREQUIRED},
|
||||
{sfCoverRateMinimum, soeREQUIRED},
|
||||
{sfCoverRateLiquidation, soeREQUIRED},
|
||||
{sfOwnerCount, soeDEFAULT},
|
||||
{sfDebtTotal, soeDEFAULT},
|
||||
{sfDebtMaximum, soeDEFAULT},
|
||||
{sfCoverAvailable, soeDEFAULT},
|
||||
{sfCoverRateMinimum, soeDEFAULT},
|
||||
{sfCoverRateLiquidation, soeDEFAULT},
|
||||
}))
|
||||
|
||||
/** A ledger object representing a loan between a Borrower and a Loan Broker
|
||||
|
||||
@@ -211,6 +211,7 @@ TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
||||
TYPED_SFIELD(sfDomainID, UINT256, 34)
|
||||
TYPED_SFIELD(sfVaultID, UINT256, 35)
|
||||
TYPED_SFIELD(sfLoanBrokerID, UINT256, 36)
|
||||
TYPED_SFIELD(sfLoanID, UINT256, 37)
|
||||
|
||||
// number (common)
|
||||
TYPED_SFIELD(sfNumber, NUMBER, 1)
|
||||
|
||||
@@ -508,7 +508,6 @@ TRANSACTION(ttVAULT_CLAWBACK, 69, VaultClawback, ({
|
||||
{sfAmount, soeOPTIONAL, soeMPTSupported},
|
||||
}))
|
||||
|
||||
#if 0
|
||||
/** This transaction creates and updates a Loan Broker */
|
||||
TRANSACTION(ttLOAN_BROKER_SET, 70, LoanBrokerSet, ({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
@@ -520,6 +519,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 70, LoanBrokerSet, ({
|
||||
{sfCoverRateLiquidation, soeDEFAULT},
|
||||
}))
|
||||
|
||||
#if 0
|
||||
/** This transaction deletes a Loan Broker */
|
||||
TRANSACTION(ttLOAN_BROKER_DELETE, 71, LoanBrokerDelete, ({
|
||||
{sfLoanBrokerID, soeREQUIRED},
|
||||
@@ -556,6 +556,28 @@ TRANSACTION(ttLOAN_SET, 74, LoanSet, ({
|
||||
{sfPaymentInterval, soeOPTIONAL},
|
||||
{sfGracePeriod, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction deletes an existing Loan */
|
||||
TRANSACTION(ttLOAN_DELETE, 75, LoanDelete, ({
|
||||
{sfLoanID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction is used to change the delinquency status of an existing Loan */
|
||||
TRANSACTION(ttLOAN_MANAGE, 76, LoanManage, ({
|
||||
{sfLoanID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** The Borrower uses this transaction to draws funds from the Loan. */
|
||||
TRANSACTION(ttLOAN_DRAW, 77, LoanDraw, ({
|
||||
{sfLoanID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** The Borrower uses this transaction to make a Payment on the Loan. */
|
||||
TRANSACTION(ttLOAN_PAY, 77, LoanPay, ({
|
||||
{sfLoanID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
}))
|
||||
#endif
|
||||
|
||||
/** This system-generated transaction type is used to update the status of the various amendments.
|
||||
|
||||
205
src/xrpld/app/tx/detail/LoanBroker.cpp
Normal file
205
src/xrpld/app/tx/detail/LoanBroker.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/app/tx/detail/LoanBroker.h>
|
||||
#include <xrpld/app/tx/detail/SignerEntries.h>
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
#include <xrpld/app/tx/detail/VaultCreate.h>
|
||||
#include <xrpld/ledger/ApplyView.h>
|
||||
#include <xrpld/ledger/View.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
bool
|
||||
lendingProtocolEnabled(PreflightContext const& ctx)
|
||||
{
|
||||
return ctx.rules.enabled(featureLendingProtocol) &&
|
||||
VaultCreate::isEnabled(ctx);
|
||||
}
|
||||
|
||||
bool
|
||||
LoanBrokerSet::isEnabled(PreflightContext const& ctx)
|
||||
{
|
||||
return lendingProtocolEnabled(ctx);
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
LoanBrokerSet::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfUniversalMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
LoanBrokerSet::doPreflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const& tx = ctx.tx;
|
||||
if (!validDataLength(tx[~sfData], maxDataPayloadLength))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(tx[~sfManagementFeeRate], 0, maxFeeRate))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(tx[~sfCoverRateMinimum], 0, maxCoverRate))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(tx[~sfCoverRateLiquidation], 0, maxCoverRate))
|
||||
return temINVALID;
|
||||
|
||||
if (tx.isFieldPresent(sfLoanBrokerID))
|
||||
{
|
||||
// Fixed fields can not be specified if we're modifying an existing
|
||||
// LoanBroker Object
|
||||
if (tx.isFieldPresent(sfManagementFeeRate) ||
|
||||
tx.isFieldPresent(sfCoverRateMinimum) ||
|
||||
tx.isFieldPresent(sfCoverRateLiquidation))
|
||||
return temINVALID;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
LoanBrokerSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const& tx = ctx.tx;
|
||||
|
||||
auto const account = tx[sfAccount];
|
||||
if (auto const brokerID = tx[~sfLoanBrokerID])
|
||||
{
|
||||
auto const sleBroker = ctx.view.read(keylet::loanbroker(*brokerID));
|
||||
if (!sleBroker)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
if (tx[sfVaultID] != sleBroker->at(sfVaultID))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Can not change VaultID on an existing LoanBroker.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
if (account != sleBroker->at(sfOwner))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Account is not the owner of the LoanBroker.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const vaultID = tx[sfVaultID];
|
||||
auto const sleVault = ctx.view.read(keylet::vault(vaultID));
|
||||
if (!sleVault)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Vault does not exist.";
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
if (account != sleVault->at(sfOwner))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Account is not the owner of the Vault.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
LoanBrokerSet::doApply()
|
||||
{
|
||||
auto const& tx = ctx_.tx;
|
||||
auto& view = ctx_.view();
|
||||
|
||||
if (auto const brokerID = tx[~sfLoanBrokerID])
|
||||
{
|
||||
// Modify an existing LoanBroker
|
||||
auto const sleBroker = view.read(keylet::loanbroker(*brokerID));
|
||||
|
||||
assert(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new LoanBroker pointing back to the given Vault
|
||||
auto const vaultID = tx[sfVaultID];
|
||||
auto const sleVault = view.read(keylet::vault(vaultID));
|
||||
auto const vaultPseudoID = sleVault->at(sfAccount);
|
||||
auto const sequence = tx.getSeqValue();
|
||||
|
||||
auto owner = view.peek(keylet::account(account_));
|
||||
auto broker =
|
||||
std::make_shared<SLE>(keylet::loanbroker(account_, sequence));
|
||||
|
||||
if (auto const ter = dirLink(view, account_, broker))
|
||||
return ter;
|
||||
if (auto const ter = dirLink(view, vaultPseudoID, broker))
|
||||
return ter;
|
||||
|
||||
adjustOwnerCount(view, owner, 1, j_);
|
||||
auto ownerCount = owner->at(sfOwnerCount);
|
||||
if (mPriorBalance < view.fees().accountReserve(ownerCount))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto maybePseudo = createPseudoAccount(
|
||||
view, broker->key(), PseudoAccountOwnerType::LoanBroker);
|
||||
if (!maybePseudo)
|
||||
return maybePseudo.error();
|
||||
auto& pseudo = *maybePseudo;
|
||||
auto pseudoId = pseudo->at(sfAccount);
|
||||
|
||||
if (auto ter = addEmptyHolding(
|
||||
view, pseudoId, mPriorBalance, sleVault->at(sfAsset), j_))
|
||||
return ter;
|
||||
|
||||
// Initialize data fields:
|
||||
broker->at(sfSequence) = sequence;
|
||||
broker->at(sfVaultID) = vaultID;
|
||||
broker->at(sfOwner) = account_;
|
||||
broker->at(sfAccount) = pseudoId;
|
||||
if (auto const data = tx[~sfData])
|
||||
broker->at(sfData) = *data;
|
||||
if (auto const rate = tx[~sfManagementFeeRate])
|
||||
broker->at(sfManagementFeeRate) = *rate;
|
||||
if (auto const debtMax = tx[~sfDebtMaximum]; debtMax && *debtMax)
|
||||
broker->at(sfDebtMaximum) = *debtMax;
|
||||
if (auto const coverMin = tx[~sfCoverRateMinimum])
|
||||
broker->at(sfCoverRateMinimum) = *coverMin;
|
||||
if (auto const coverLiq = tx[~sfCoverRateLiquidation])
|
||||
broker->at(sfCoverRateLiquidation) = *coverLiq;
|
||||
|
||||
view.insert(broker);
|
||||
}
|
||||
|
||||
return temDISABLED;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace ripple
|
||||
60
src/xrpld/app/tx/detail/LoanBroker.h
Normal file
60
src/xrpld/app/tx/detail/LoanBroker.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TX_LOANBROKER_H_INCLUDED
|
||||
#define RIPPLE_TX_LOANBROKER_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Lending protocol has dependencies, so capture them here.
|
||||
bool
|
||||
lendingProtocolEnabled(PreflightContext const& ctx);
|
||||
|
||||
class LoanBrokerSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
isEnabled(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
doPreflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -205,6 +205,16 @@ Transactor::Transactor(ApplyContext& ctx)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Transactor::validDataLength(
|
||||
std::optional<Slice> const& slice,
|
||||
std::size_t maxLength)
|
||||
{
|
||||
if (!slice)
|
||||
return true;
|
||||
return !slice->empty() && slice->length() <= maxLength;
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
|
||||
@@ -146,10 +146,6 @@ public:
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
// Base class always returns true
|
||||
static bool
|
||||
isEnabled(PreflightContext const& ctx);
|
||||
|
||||
/* Do NOT define a preflight function in a derived class.
|
||||
Instead, define
|
||||
|
||||
@@ -212,6 +208,17 @@ protected:
|
||||
Fees const& fees,
|
||||
ApplyFlags flags);
|
||||
|
||||
// Base class always returns true
|
||||
static bool
|
||||
isEnabled(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
validDataLength(std::optional<Slice> const& slice, std::size_t maxLength);
|
||||
|
||||
template <class T>
|
||||
static bool
|
||||
validNumericRange(std::optional<T> value, T min, T max);
|
||||
|
||||
private:
|
||||
std::pair<TER, XRPAmount>
|
||||
reset(XRPAmount fee);
|
||||
@@ -272,6 +279,15 @@ Transactor::preflight(PreflightContext const& ctx)
|
||||
return detail::preflight2(ctx);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
Transactor::validNumericRange(std::optional<T> value, T min, T max)
|
||||
{
|
||||
if (!value)
|
||||
return true;
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -53,11 +53,8 @@ VaultCreate::getFlagsMask(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
VaultCreate::doPreflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (auto const data = ctx.tx[~sfData])
|
||||
{
|
||||
if (data->empty() || data->length() > maxDataPayloadLength)
|
||||
if (!validDataLength(ctx.tx[~sfData], maxDataPayloadLength))
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (auto const withdrawalPolicy = ctx.tx[~sfWithdrawalPolicy])
|
||||
{
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <xrpld/app/tx/detail/DepositPreauth.h>
|
||||
#include <xrpld/app/tx/detail/Escrow.h>
|
||||
#include <xrpld/app/tx/detail/LedgerStateFix.h>
|
||||
#include <xrpld/app/tx/detail/LoanBroker.h>
|
||||
#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
||||
#include <xrpld/app/tx/detail/MPTokenIssuanceCreate.h>
|
||||
#include <xrpld/app/tx/detail/MPTokenIssuanceDestroy.h>
|
||||
|
||||
Reference in New Issue
Block a user