xrpld
Loading...
Searching...
No Matches
LoanBrokerCoverDeposit.cpp
1#include <xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/ledger/helpers/LendingHelpers.h>
7#include <xrpl/ledger/helpers/TokenHelpers.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/Indexes.h>
10#include <xrpl/protocol/SField.h>
11#include <xrpl/protocol/STAmount.h>
12#include <xrpl/protocol/STLedgerEntry.h>
13#include <xrpl/protocol/STTakesAsset.h>
14#include <xrpl/protocol/STTx.h>
15#include <xrpl/protocol/TER.h>
16#include <xrpl/protocol/XRPAmount.h>
17#include <xrpl/tx/Transactor.h>
18
19namespace xrpl {
20
21bool
26
29{
30 if (ctx.tx[sfLoanBrokerID] == beast::kZero)
31 return temINVALID;
32
33 auto const dstAmount = ctx.tx[sfAmount];
34 if (dstAmount <= beast::kZero)
35 return temBAD_AMOUNT;
36
37 if (!isLegalNet(dstAmount))
38 return temBAD_AMOUNT;
39
40 return tesSUCCESS;
41}
42
43TER
45{
46 auto const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0);
47 auto const fix330Enabled = ctx.view.rules().enabled(fixCleanup3_3_0);
48 auto const& tx = ctx.tx;
49
50 auto const account = tx[sfAccount];
51 auto const brokerID = tx[sfLoanBrokerID];
52 auto const amount = tx[sfAmount];
53
54 auto const sleBroker = ctx.view.read(keylet::loanBroker(brokerID));
55 if (!sleBroker)
56 {
57 JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
58 return tecNO_ENTRY;
59 }
60 if (account != sleBroker->at(sfOwner))
61 {
62 JLOG(ctx.j.warn()) << "Account is not the owner of the LoanBroker.";
63 return tecNO_PERMISSION;
64 }
65 auto const vault = ctx.view.read(keylet::vault(sleBroker->at(sfVaultID)));
66 if (!vault)
67 {
68 // LCOV_EXCL_START
69 JLOG(ctx.j.fatal()) << "Vault is missing for Broker " << brokerID;
70 return tefBAD_LEDGER;
71 // LCOV_EXCL_STOP
72 }
73
74 auto const vaultAsset = vault->at(sfAsset);
75 if (amount.asset() != vaultAsset)
76 return tecWRONG_ASSET;
77
78 auto const pseudoAccountID = sleBroker->at(sfAccount);
79 // Cannot transfer a non-transferable Asset
80 if (auto const ret = canTransfer(ctx.view, vaultAsset, account, pseudoAccountID))
81 return ret;
82
83 if (fix330Enabled)
84 {
85 if (auto const ret = checkDepositFreeze(ctx.view, account, pseudoAccountID, vaultAsset))
86 return ret;
87 }
88 else
89 {
90 if (auto const ret = checkFrozen(ctx.view, account, vaultAsset))
91 return ret;
92
93 if (auto const ret = checkDeepFrozen(ctx.view, pseudoAccountID, vaultAsset))
94 return ret;
95 }
96
97 // Cannot transfer unauthorized asset
98 if (auto const ret = requireAuth(ctx.view, vaultAsset, account, AuthType::StrongAuth))
99 return ret;
100
101 // Deposit must round the amount Downward to cover scale and then reuse that rounded
102 // value for the actual transfer in doApply — otherwise implicit round-to-nearest during
103 // `sfCoverAvailable +=` could credit the broker more than the depositor paid Computing it
104 // here in preclaim lets us reject sub-cover-scale dust early with tecPRECISION_LOSS instead of
105 // failing only in doApply.
106 auto const roundedAmount = [&]() -> STAmount {
107 if (!fix320Enabled)
108 return tx[sfAmount];
109
110 return roundToScale(
111 tx[sfAmount],
112 scale(sleBroker->at(sfCoverAvailable), vaultAsset),
114 }();
115
116 if (fix320Enabled && roundedAmount == beast::kZero)
117 {
118 JLOG(ctx.j.warn()) << "LoanBrokerCoverDeposit: deposit amount: " << tx[sfAmount]
119 << " is zero at loan broker scale";
120 return tecPRECISION_LOSS;
121 }
122
123 if (accountHolds(
124 ctx.view,
125 account,
126 vaultAsset,
129 ctx.j,
130 SpendableHandling::FullBalance) < roundedAmount)
132
133 return tesSUCCESS;
134}
135
136TER
138{
139 auto const& tx = ctx_.tx;
140
141 auto const brokerID = tx[sfLoanBrokerID];
142 auto broker = view().peek(keylet::loanBroker(brokerID));
143 if (!broker)
144 return tecINTERNAL; // LCOV_EXCL_LINE
145
146 auto const vault = view().read(keylet::vault(broker->at(sfVaultID)));
147 if (!vault)
148 return tecINTERNAL; // LCOV_EXCL_LINE
149
150 auto const vaultAsset = vault->at(sfAsset);
151 auto const brokerPseudoID = broker->at(sfAccount);
152
153 // Re-round here (matches preclaim) so the same cover-scale-quantized
154 // value drives both the trustline transfer and the cover increment;
155 // see the rationale comment in preclaim.
156 bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0);
157 auto const amount = [&]() -> STAmount {
158 if (!fix320Enabled)
159 return tx[sfAmount];
160
161 return roundToScale(
162 tx[sfAmount],
163 scale(broker->at(sfCoverAvailable), vaultAsset),
165 }();
166
167 // We validated zero-amount in preclaim, if we ended up with zero now, fail hard.
168 if (amount == beast::kZero)
169 {
170 // LCOV_EXCL_START
171 JLOG(j_.error()) << "LoanBrokerCoverDeposit: deposit amount: " << tx[sfAmount]
172 << " is zero";
173 return tecINTERNAL;
174 // LCOV_EXCL_STOP
175 }
176
177 // Transfer assets from depositor to pseudo-account.
178 if (auto ter =
179 accountSend(view(), accountID_, brokerPseudoID, amount, j_, WaiveTransferFee::Yes))
180 return ter;
181
182 // Increase the LoanBroker's CoverAvailable by Amount
183 broker->at(sfCoverAvailable) += amount;
184 view().update(broker);
185
186 associateAsset(*broker, vaultAsset);
187
188 return tesSUCCESS;
189}
190
191void
193{
194 // No transaction-specific invariants yet (future work).
195}
196
197bool
199 STTx const&,
200 TER,
201 XRPAmount,
202 ReadView const&,
203 beast::Journal const&)
204{
205 // No transaction-specific invariants yet (future work).
206 return true;
207}
208
209//------------------------------------------------------------------------------
210
211} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
Stream fatal() const
Definition Journal.h:321
Stream warn() const
Definition Journal.h:309
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void update(SLE::ref sle)=0
Indicate changes to a peeked SLE.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static bool checkExtraFeatures(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:171
std::shared_ptr< STLedgerEntry const > const & const_ref
beast::Journal const j_
Definition Transactor.h:118
ApplyView & view()
Definition Transactor.h:136
AccountID const accountID_
Definition Transactor.h:120
ApplyContext & ctx_
Definition Transactor.h:116
Keylet loanBroker(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:557
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:551
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TER checkDeepFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
int scale(Number const &number, Asset const &asset)
Get the scale of a Number for a given asset.
Definition STAmount.h:779
@ tefBAD_LEDGER
Definition TER.h:160
bool isLegalNet(STAmount const &value)
Definition STAmount.h:598
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to, WaiveMPTCanTransfer waive=WaiveMPTCanTransfer::No, std::uint8_t depth=0)
Check whether to may receive the given MPT from from.
STAmount roundToScale(STAmount const &value, std::int32_t scale, Number::RoundingMode rounding=Number::getround())
Round an arbitrary precision Amount to the precision of an STAmount that has a given exponent.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:594
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
TER checkDepositFreeze(ReadView const &view, AccountID const &srcAcct, AccountID const &dstAcct, Asset const &asset)
Checks freeze compliance for depositing an asset into a pseudo-account (e.g.
@ temINVALID
Definition TER.h:96
@ temBAD_AMOUNT
Definition TER.h:75
TERSubset< CanCvtToTER > TER
Definition TER.h:634
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
@ tecWRONG_ASSET
Definition TER.h:358
@ tecNO_ENTRY
Definition TER.h:304
@ tecINTERNAL
Definition TER.h:308
@ tecINSUFFICIENT_FUNDS
Definition TER.h:323
@ tecPRECISION_LOSS
Definition TER.h:361
@ tecNO_PERMISSION
Definition TER.h:303
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
@ tesSUCCESS
Definition TER.h:240
bool checkLendingProtocolDependencies(Rules const &rules, STTx const &tx)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:18