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