rippled
Loading...
Searching...
No Matches
LoanBrokerSet.cpp
1#include <xrpl/tx/transactors/lending/LoanBrokerSet.h>
2//
3#include <xrpl/ledger/helpers/AccountRootHelpers.h>
4#include <xrpl/ledger/helpers/TokenHelpers.h>
5#include <xrpl/protocol/STTakesAsset.h>
6#include <xrpl/tx/transactors/lending/LendingHelpers.h>
7
8namespace xrpl {
9
10bool
15
18{
19 using namespace Lending;
20
21 auto const& tx = ctx.tx;
22 if (auto const data = tx[~sfData];
23 data && !data->empty() && !validDataLength(tx[~sfData], maxDataPayloadLength))
24 return temINVALID;
25 if (!validNumericRange(tx[~sfManagementFeeRate], maxManagementFeeRate))
26 return temINVALID;
27 if (!validNumericRange(tx[~sfCoverRateMinimum], maxCoverRate))
28 return temINVALID;
29 if (!validNumericRange(tx[~sfCoverRateLiquidation], maxCoverRate))
30 return temINVALID;
31 if (!validNumericRange(tx[~sfDebtMaximum], Number(maxMPTokenAmount), Number(0)))
32 return temINVALID;
33
34 if (tx.isFieldPresent(sfLoanBrokerID))
35 {
36 // Fixed fields can not be specified if we're modifying an existing
37 // LoanBroker Object
38 if (tx.isFieldPresent(sfManagementFeeRate) || tx.isFieldPresent(sfCoverRateMinimum) ||
39 tx.isFieldPresent(sfCoverRateLiquidation))
40 return temINVALID;
41
42 if (tx[sfLoanBrokerID] == beast::zero)
43 return temINVALID;
44 }
45
46 if (auto const vaultID = tx.at(~sfVaultID))
47 {
48 if (*vaultID == beast::zero)
49 return temINVALID;
50 }
51
52 {
53 auto const minimumZero = tx[~sfCoverRateMinimum].value_or(0) == 0;
54 auto const liquidationZero = tx[~sfCoverRateLiquidation].value_or(0) == 0;
55 // Both must be zero or non-zero.
56 if (minimumZero != liquidationZero)
57 {
58 return temINVALID;
59 }
60 }
61
62 return tesSUCCESS;
63}
64
67{
68 static std::vector<OptionaledField<STNumber>> const valueFields{~sfDebtMaximum};
69
70 return valueFields;
71}
72
73TER
75{
76 auto const& tx = ctx.tx;
77
78 auto const account = tx[sfAccount];
79 auto const vaultID = tx[sfVaultID];
80
81 auto const sleVault = ctx.view.read(keylet::vault(vaultID));
82 if (!sleVault)
83 {
84 JLOG(ctx.j.warn()) << "Vault does not exist.";
85 return tecNO_ENTRY;
86 }
87 Asset const asset = sleVault->at(sfAsset);
88
89 if (account != sleVault->at(sfOwner))
90 {
91 JLOG(ctx.j.warn()) << "Account is not the owner of the Vault.";
92 return tecNO_PERMISSION;
93 }
94
95 if (auto const brokerID = tx[~sfLoanBrokerID])
96 {
97 // Updating an existing Broker
98
99 auto const sleBroker = ctx.view.read(keylet::loanbroker(*brokerID));
100 if (!sleBroker)
101 {
102 JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
103 return tecNO_ENTRY;
104 }
105 if (vaultID != sleBroker->at(sfVaultID))
106 {
107 JLOG(ctx.j.warn()) << "Can not change VaultID on an existing LoanBroker.";
108 return tecNO_PERMISSION;
109 }
110 if (account != sleBroker->at(sfOwner))
111 {
112 JLOG(ctx.j.warn()) << "Account is not the owner of the LoanBroker.";
113 return tecNO_PERMISSION;
114 }
115
116 if (auto const debtMax = tx[~sfDebtMaximum])
117 {
118 // Can't reduce the debt maximum below the current total debt
119 auto const currentDebtTotal = sleBroker->at(sfDebtTotal);
120 if (*debtMax != 0 && *debtMax < currentDebtTotal)
121 {
122 JLOG(ctx.j.warn()) << "Cannot reduce DebtMaximum below current DebtTotal.";
123 return tecLIMIT_EXCEEDED;
124 }
125 }
126 }
127 else
128 {
129 if (auto const ter = canAddHolding(ctx.view, asset))
130 return ter;
131
132 if (auto const ter = checkFrozen(ctx.view, sleVault->at(sfAccount), sleVault->at(sfAsset)))
133 {
134 JLOG(ctx.j.warn()) << "Vault pseudo-account is frozen.";
135 return ter;
136 }
137 }
138
139 // Check that relevant values can be represented as the vault asset
140 // type. This is mostly only relevant for integral (non-IOU) types
141 for (auto const& field : getValueFields())
142 {
143 if (auto const value = tx[field]; value && STAmount{asset, *value} != *value)
144 {
145 JLOG(ctx.j.warn()) << field.f->getName() << " (" << *value
146 << ") can not be represented as a(n) " << to_string(asset) << ".";
147 return tecPRECISION_LOSS;
148 }
149 }
150
151 return tesSUCCESS;
152}
153
154TER
156{
157 auto const& tx = ctx_.tx;
158 auto& view = ctx_.view();
159
160 if (auto const brokerID = tx[~sfLoanBrokerID])
161 {
162 // Modify an existing LoanBroker
163 auto broker = view.peek(keylet::loanbroker(*brokerID));
164 if (!broker)
165 {
166 // This should be impossible
167 // LCOV_EXCL_START
168 JLOG(j_.fatal()) << "LoanBroker does not exist.";
169 return tefBAD_LEDGER;
170 // LCOV_EXCL_STOP
171 }
172
173 auto const vault = view.read(keylet::vault(broker->at(sfVaultID)));
174 if (!vault)
175 return tecINTERNAL; // LCOV_EXCL_LINE
176
177 auto const vaultAsset = vault->at(sfAsset);
178
179 if (auto const data = tx[~sfData])
180 broker->at(sfData) = *data;
181 if (auto const debtMax = tx[~sfDebtMaximum])
182 broker->at(sfDebtMaximum) = *debtMax;
183
184 view.update(broker);
185
186 associateAsset(*broker, vaultAsset);
187 }
188 else
189 {
190 // Create a new LoanBroker pointing back to the given Vault
191 auto const vaultID = tx[sfVaultID];
192 auto const sleVault = view.read(keylet::vault(vaultID));
193 if (!sleVault)
194 {
195 // This should be impossible
196 // LCOV_EXCL_START
197 JLOG(j_.fatal()) << "Vault does not exist.";
198 return tefBAD_LEDGER;
199 // LCOV_EXCL_STOP
200 }
201 auto const vaultPseudoID = sleVault->at(sfAccount);
202 auto const vaultAsset = sleVault->at(sfAsset);
203 auto const sequence = tx.getSeqValue();
204
205 auto owner = view.peek(keylet::account(account_));
206 if (!owner)
207 {
208 // This should be impossible
209 // LCOV_EXCL_START
210 JLOG(j_.fatal()) << "Account does not exist.";
211 return tefBAD_LEDGER;
212 // LCOV_EXCL_STOP
213 }
214 auto broker = std::make_shared<SLE>(keylet::loanbroker(account_, sequence));
215
216 if (auto const ter = dirLink(view, account_, broker))
217 return ter; // LCOV_EXCL_LINE
218 if (auto const ter = dirLink(view, vaultPseudoID, broker, sfVaultNode))
219 return ter; // LCOV_EXCL_LINE
220
221 // Increases the owner count by two: one for the LoanBroker object, and
222 // one for the pseudo-account.
223 adjustOwnerCount(view, owner, 2, j_);
224 auto const ownerCount = owner->at(sfOwnerCount);
225 if (preFeeBalance_ < view.fees().accountReserve(ownerCount))
227
228 auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID);
229 if (!maybePseudo)
230 return maybePseudo.error(); // LCOV_EXCL_LINE
231 auto& pseudo = *maybePseudo;
232 auto pseudoId = pseudo->at(sfAccount);
233
234 if (auto ter = addEmptyHolding(view, pseudoId, preFeeBalance_, sleVault->at(sfAsset), j_))
235 return ter;
236
237 // Initialize data fields:
238 broker->at(sfSequence) = sequence;
239 broker->at(sfVaultID) = vaultID;
240 broker->at(sfOwner) = account_;
241 broker->at(sfAccount) = pseudoId;
242 // The LoanSequence indexes loans created by this broker, starting at 1
243 broker->at(sfLoanSequence) = 1;
244 if (auto const data = tx[~sfData])
245 broker->at(sfData) = *data;
246 if (auto const rate = tx[~sfManagementFeeRate])
247 broker->at(sfManagementFeeRate) = *rate;
248 if (auto const debtMax = tx[~sfDebtMaximum])
249 broker->at(sfDebtMaximum) = *debtMax;
250 if (auto const coverMin = tx[~sfCoverRateMinimum])
251 broker->at(sfCoverRateMinimum) = *coverMin;
252 if (auto const coverLiq = tx[~sfCoverRateLiquidation])
253 broker->at(sfCoverRateLiquidation) = *coverLiq;
254
255 view.insert(broker);
256
257 associateAsset(*broker, vaultAsset);
258 }
259
260 return tesSUCCESS;
261}
262
263//------------------------------------------------------------------------------
264
265} // namespace xrpl
Stream fatal() const
Definition Journal.h:325
Stream warn() const
Definition Journal.h:313
STTx const & tx
ApplyView & view()
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static std::vector< OptionaledField< STNumber > > const & getValueFields()
TER doApply() override
static bool checkExtraFeatures(PreflightContext const &ctx)
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
AccountID const account_
Definition Transactor.h:116
beast::Journal const j_
Definition Transactor.h:114
ApplyView & view()
Definition Transactor.h:132
XRPAmount preFeeBalance_
Definition Transactor.h:117
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:112
static bool validNumericRange(std::optional< T > value, T max, T min=T{})
Definition Transactor.h:400
T is_same_v
Keylet loanbroker(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:510
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:504
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, MPTIssue const &mptIssue, beast::Journal journal)
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:234
@ tefBAD_LEDGER
Definition TER.h:150
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:238
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
TER canAddHolding(ReadView const &view, MPTIssue const &mptIssue)
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
Definition View.cpp:296
@ temINVALID
Definition TER.h:90
@ tecNO_ENTRY
Definition TER.h:287
@ tecINTERNAL
Definition TER.h:291
@ tecPRECISION_LOSS
Definition TER.h:344
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
@ tecLIMIT_EXCEEDED
Definition TER.h:342
@ tecNO_PERMISSION
Definition TER.h:286
Expected< std::shared_ptr< SLE >, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
@ tesSUCCESS
Definition TER.h:225
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:57
ReadView const & view
Definition Transactor.h:60
beast::Journal const j
Definition Transactor.h:65
State information when preflighting a tx.
Definition Transactor.h:14