Skip to content

Commit bc6a745

Browse files
committed
[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.
1 parent 1eaf437 commit bc6a745

File tree

13 files changed

+378
-28
lines changed

13 files changed

+378
-28
lines changed

include/xrpl/protocol/Protocol.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@ std::size_t constexpr maxDeletableTokenOfferEntries = 500;
8282
*/
8383
std::uint16_t constexpr maxTransferFee = 50000;
8484

85+
/** The maximum management fee rate allowed in lending.
86+
87+
TODO: Is this a good name?
88+
89+
Valid values for the the management fee charged by the Lending Protocol are
90+
between 0 and 10000 inclusive. A value of 1 is equivalent to 1/10 basis
91+
point fee or 0.001%.
92+
*/
93+
std::uint16_t constexpr maxFeeRate = 10'000;
94+
95+
/** The maximum coverage rate allowed in lending.
96+
97+
TODO: Is this a good name?
98+
99+
Valid values for the coverage rate charged by the Lending Protocol for first
100+
loss capital operations are between 0 and 100000 inclusive. A value of 1 is
101+
equivalent to 1/10 bps or 0.001%.
102+
*/
103+
std::uint16_t constexpr maxCoverRate = 100'000;
104+
85105
/** The maximum length of a URI inside an NFT */
86106
std::size_t constexpr maxTokenURILength = 256;
87107

include/xrpl/protocol/TxFlags.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,13 @@ constexpr std::uint32_t const tfVaultCreateMask = ~(tfUniversal | tfVaultPrivate
237237
// True, indicates the load supports overpayments
238238
constexpr std::uint32_t const tfLoanOverpayment = 0x00010000;
239239
constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal | tfLoanOverpayment);
240+
241+
// LoanManage flags:
242+
constexpr std::uint32_t const tfLoanDefault = 0x00010000;
243+
constexpr std::uint32_t const tfLoanImpair = 0x00010000;
244+
constexpr std::uint32_t const tfLoanUnimpair = 0x00010000;
245+
constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair);
246+
240247
// clang-format on
241248

242249
} // namespace ripple

include/xrpl/protocol/detail/ledger_entries.macro

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -501,12 +501,12 @@ LEDGER_ENTRY(ltLOAN_BROKER, 0x0084, LoanBroker, loan_broker, ({
501501
{sfOwner, soeREQUIRED},
502502
{sfData, soeDEFAULT},
503503
{sfManagementFeeRate, soeDEFAULT},
504-
{sfOwnerCount, soeREQUIRED},
505-
{sfDebtTotal, soeREQUIRED},
506-
{sfDebtMaximum, soeREQUIRED},
507-
{sfCoverAvailable, soeREQUIRED},
508-
{sfCoverRateMinimum, soeREQUIRED},
509-
{sfCoverRateLiquidation, soeREQUIRED},
504+
{sfOwnerCount, soeDEFAULT},
505+
{sfDebtTotal, soeDEFAULT},
506+
{sfDebtMaximum, soeDEFAULT},
507+
{sfCoverAvailable, soeDEFAULT},
508+
{sfCoverRateMinimum, soeDEFAULT},
509+
{sfCoverRateLiquidation, soeDEFAULT},
510510
}))
511511

512512
/** A ledger object representing a loan between a Borrower and a Loan Broker

include/xrpl/protocol/detail/sfields.macro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
210210
TYPED_SFIELD(sfDomainID, UINT256, 34)
211211
TYPED_SFIELD(sfVaultID, UINT256, 35)
212212
TYPED_SFIELD(sfLoanBrokerID, UINT256, 36)
213+
TYPED_SFIELD(sfLoanID, UINT256, 37)
213214

214215
// number (common)
215216
TYPED_SFIELD(sfNumber, NUMBER, 1)

include/xrpl/protocol/detail/transactions.macro

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,6 @@ TRANSACTION(ttVAULT_CLAWBACK, 69, VaultClawback, ({
508508
{sfAmount, soeOPTIONAL, soeMPTSupported},
509509
}))
510510

511-
#if 0
512511
/** This transaction creates and updates a Loan Broker */
513512
TRANSACTION(ttLOAN_BROKER_SET, 70, LoanBrokerSet, ({
514513
{sfVaultID, soeREQUIRED},
@@ -520,6 +519,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 70, LoanBrokerSet, ({
520519
{sfCoverRateLiquidation, soeDEFAULT},
521520
}))
522521

522+
#if 0
523523
/** This transaction deletes a Loan Broker */
524524
TRANSACTION(ttLOAN_BROKER_DELETE, 71, LoanBrokerDelete, ({
525525
{sfLoanBrokerID, soeREQUIRED},
@@ -556,6 +556,28 @@ TRANSACTION(ttLOAN_SET, 74, LoanSet, ({
556556
{sfPaymentInterval, soeOPTIONAL},
557557
{sfGracePeriod, soeOPTIONAL},
558558
}))
559+
560+
/** This transaction deletes an existing Loan */
561+
TRANSACTION(ttLOAN_DELETE, 75, LoanDelete, ({
562+
{sfLoanID, soeREQUIRED},
563+
}))
564+
565+
/** This transaction is used to change the delinquency status of an existing Loan */
566+
TRANSACTION(ttLOAN_MANAGE, 76, LoanManage, ({
567+
{sfLoanID, soeREQUIRED},
568+
}))
569+
570+
/** The Borrower uses this transaction to draws funds from the Loan. */
571+
TRANSACTION(ttLOAN_DRAW, 77, LoanDraw, ({
572+
{sfLoanID, soeREQUIRED},
573+
{sfAmount, soeREQUIRED},
574+
}))
575+
576+
/** The Borrower uses this transaction to make a Payment on the Loan. */
577+
TRANSACTION(ttLOAN_PAY, 77, LoanPay, ({
578+
{sfLoanID, soeREQUIRED},
579+
{sfAmount, soeREQUIRED},
580+
}))
559581
#endif
560582

561583
/** This system-generated transaction type is used to update the status of the various amendments.
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
//------------------------------------------------------------------------------
2+
/*
3+
This file is part of rippled: https://github.com/ripple/rippled
4+
Copyright (c) 2022 Ripple Labs Inc.
5+
6+
Permission to use, copy, modify, and/or distribute this software for any
7+
purpose with or without fee is hereby granted, provided that the above
8+
copyright notice and this permission notice appear in all copies.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
//==============================================================================
19+
20+
#include <xrpld/app/tx/detail/LoanBroker.h>
21+
#include <xrpld/app/tx/detail/SignerEntries.h>
22+
#include <xrpld/app/tx/detail/Transactor.h>
23+
#include <xrpld/app/tx/detail/VaultCreate.h>
24+
#include <xrpld/ledger/ApplyView.h>
25+
#include <xrpld/ledger/View.h>
26+
27+
#include <xrpl/basics/Log.h>
28+
#include <xrpl/basics/Number.h>
29+
#include <xrpl/basics/chrono.h>
30+
#include <xrpl/beast/utility/Journal.h>
31+
#include <xrpl/beast/utility/instrumentation.h>
32+
#include <xrpl/protocol/AccountID.h>
33+
#include <xrpl/protocol/Feature.h>
34+
#include <xrpl/protocol/Indexes.h>
35+
#include <xrpl/protocol/PublicKey.h>
36+
#include <xrpl/protocol/SField.h>
37+
#include <xrpl/protocol/STAmount.h>
38+
#include <xrpl/protocol/STObject.h>
39+
#include <xrpl/protocol/STXChainBridge.h>
40+
#include <xrpl/protocol/TER.h>
41+
#include <xrpl/protocol/TxFlags.h>
42+
#include <xrpl/protocol/XRPAmount.h>
43+
44+
namespace ripple {
45+
46+
bool
47+
lendingProtocolEnabled(PreflightContext const& ctx)
48+
{
49+
return ctx.rules.enabled(featureLendingProtocol) &&
50+
VaultCreate::isEnabled(ctx);
51+
}
52+
53+
bool
54+
LoanBrokerSet::isEnabled(PreflightContext const& ctx)
55+
{
56+
return lendingProtocolEnabled(ctx);
57+
}
58+
59+
std::uint32_t
60+
LoanBrokerSet::getFlagsMask(PreflightContext const& ctx)
61+
{
62+
return tfUniversalMask;
63+
}
64+
65+
NotTEC
66+
LoanBrokerSet::doPreflight(PreflightContext const& ctx)
67+
{
68+
auto const& tx = ctx.tx;
69+
if (!validDataLength(tx[~sfData], maxDataPayloadLength))
70+
return temINVALID;
71+
if (!validNumericRange(tx[~sfManagementFeeRate], 0, maxFeeRate))
72+
return temINVALID;
73+
if (!validNumericRange(tx[~sfCoverRateMinimum], 0, maxCoverRate))
74+
return temINVALID;
75+
if (!validNumericRange(tx[~sfCoverRateLiquidation], 0, maxCoverRate))
76+
return temINVALID;
77+
78+
if (tx.isFieldPresent(sfLoanBrokerID))
79+
{
80+
// Fixed fields can not be specified if we're modifying an existing
81+
// LoanBroker Object
82+
if (tx.isFieldPresent(sfManagementFeeRate) ||
83+
tx.isFieldPresent(sfCoverRateMinimum) ||
84+
tx.isFieldPresent(sfCoverRateLiquidation))
85+
return temINVALID;
86+
}
87+
88+
return tesSUCCESS;
89+
}
90+
91+
TER
92+
LoanBrokerSet::preclaim(PreclaimContext const& ctx)
93+
{
94+
auto const& tx = ctx.tx;
95+
96+
auto const account = tx[sfAccount];
97+
if (auto const brokerID = tx[~sfLoanBrokerID])
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 (tx[sfVaultID] != sleBroker->at(sfVaultID))
106+
{
107+
JLOG(ctx.j.warn())
108+
<< "Can not change VaultID on an existing LoanBroker.";
109+
return tecNO_PERMISSION;
110+
}
111+
if (account != sleBroker->at(sfOwner))
112+
{
113+
JLOG(ctx.j.warn()) << "Account is not the owner of the LoanBroker.";
114+
return tecNO_PERMISSION;
115+
}
116+
}
117+
else
118+
{
119+
auto const vaultID = tx[sfVaultID];
120+
auto const sleVault = ctx.view.read(keylet::vault(vaultID));
121+
if (!sleVault)
122+
{
123+
JLOG(ctx.j.warn()) << "Vault does not exist.";
124+
return tecNO_ENTRY;
125+
}
126+
if (account != sleVault->at(sfOwner))
127+
{
128+
JLOG(ctx.j.warn()) << "Account is not the owner of the Vault.";
129+
return tecNO_PERMISSION;
130+
}
131+
}
132+
return tesSUCCESS;
133+
}
134+
135+
TER
136+
LoanBrokerSet::doApply()
137+
{
138+
auto const& tx = ctx_.tx;
139+
auto& view = ctx_.view();
140+
141+
if (auto const brokerID = tx[~sfLoanBrokerID])
142+
{
143+
// Modify an existing LoanBroker
144+
auto const sleBroker = view.read(keylet::loanbroker(*brokerID));
145+
146+
assert(0);
147+
}
148+
else
149+
{
150+
// Create a new LoanBroker pointing back to the given Vault
151+
auto const vaultID = tx[sfVaultID];
152+
auto const sleVault = view.read(keylet::vault(vaultID));
153+
auto const vaultPseudoID = sleVault->at(sfAccount);
154+
auto const sequence = tx.getSeqValue();
155+
156+
auto owner = view.peek(keylet::account(account_));
157+
auto broker =
158+
std::make_shared<SLE>(keylet::loanbroker(account_, sequence));
159+
160+
if (auto const ter = dirLink(view, account_, broker))
161+
return ter;
162+
if (auto const ter = dirLink(view, vaultPseudoID, broker))
163+
return ter;
164+
165+
adjustOwnerCount(view, owner, 1, j_);
166+
auto ownerCount = owner->at(sfOwnerCount);
167+
if (mPriorBalance < view.fees().accountReserve(ownerCount))
168+
return tecINSUFFICIENT_RESERVE;
169+
170+
auto maybePseudo = createPseudoAccount(
171+
view, broker->key(), PseudoAccountOwnerType::LoanBroker);
172+
if (!maybePseudo)
173+
return maybePseudo.error();
174+
auto& pseudo = *maybePseudo;
175+
auto pseudoId = pseudo->at(sfAccount);
176+
177+
if (auto ter = addEmptyHolding(
178+
view, pseudoId, mPriorBalance, sleVault->at(sfAsset), j_))
179+
return ter;
180+
181+
// Initialize data fields:
182+
broker->at(sfSequence) = sequence;
183+
broker->at(sfVaultID) = vaultID;
184+
broker->at(sfOwner) = account_;
185+
broker->at(sfAccount) = pseudoId;
186+
if (auto const data = tx[~sfData])
187+
broker->at(sfData) = *data;
188+
if (auto const rate = tx[~sfManagementFeeRate])
189+
broker->at(sfManagementFeeRate) = *rate;
190+
if (auto const debtMax = tx[~sfDebtMaximum]; debtMax && *debtMax)
191+
broker->at(sfDebtMaximum) = *debtMax;
192+
if (auto const coverMin = tx[~sfCoverRateMinimum])
193+
broker->at(sfCoverRateMinimum) = *coverMin;
194+
if (auto const coverLiq = tx[~sfCoverRateLiquidation])
195+
broker->at(sfCoverRateLiquidation) = *coverLiq;
196+
197+
view.insert(broker);
198+
}
199+
200+
return temDISABLED;
201+
}
202+
203+
//------------------------------------------------------------------------------
204+
205+
} // namespace ripple
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//------------------------------------------------------------------------------
2+
/*
3+
This file is part of rippled: https://github.com/ripple/rippled
4+
Copyright (c) 2022 Ripple Labs Inc.
5+
6+
Permission to use, copy, modify, and/or distribute this software for any
7+
purpose with or without fee is hereby granted, provided that the above
8+
copyright notice and this permission notice appear in all copies.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
//==============================================================================
19+
20+
#ifndef RIPPLE_TX_LOANBROKER_H_INCLUDED
21+
#define RIPPLE_TX_LOANBROKER_H_INCLUDED
22+
23+
#include <xrpld/app/tx/detail/Transactor.h>
24+
25+
namespace ripple {
26+
27+
// Lending protocol has dependencies, so capture them here.
28+
bool
29+
lendingProtocolEnabled(PreflightContext const& ctx);
30+
31+
class LoanBrokerSet : public Transactor
32+
{
33+
public:
34+
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
35+
36+
explicit LoanBrokerSet(ApplyContext& ctx) : Transactor(ctx)
37+
{
38+
}
39+
40+
static bool
41+
isEnabled(PreflightContext const& ctx);
42+
43+
static std::uint32_t
44+
getFlagsMask(PreflightContext const& ctx);
45+
46+
static NotTEC
47+
doPreflight(PreflightContext const& ctx);
48+
49+
static TER
50+
preclaim(PreclaimContext const& ctx);
51+
52+
TER
53+
doApply() override;
54+
};
55+
56+
//------------------------------------------------------------------------------
57+
58+
} // namespace ripple
59+
60+
#endif

src/xrpld/app/tx/detail/Transactor.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ Transactor::Transactor(ApplyContext& ctx)
205205
{
206206
}
207207

208+
bool
209+
Transactor::validDataLength(
210+
std::optional<Slice> const& slice,
211+
std::size_t maxLength)
212+
{
213+
if (!slice)
214+
return true;
215+
return !slice->empty() && slice->length() <= maxLength;
216+
}
217+
208218
XRPAmount
209219
Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
210220
{

0 commit comments

Comments
 (0)