1#include <test/app/lending/LoanTestBase.h>
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/fee.h>
7#include <test/jtx/noop.h>
8#include <test/jtx/sponsor.h>
9#include <test/jtx/ter.h>
10#include <test/jtx/txflags.h>
11#include <test/jtx/vault.h>
13#include <xrpl/basics/Number.h>
14#include <xrpl/basics/chrono.h>
15#include <xrpl/beast/unit_test/suite.h>
16#include <xrpl/beast/utility/Journal.h>
17#include <xrpl/beast/utility/Zero.h>
18#include <xrpl/core/ServiceRegistry.h>
19#include <xrpl/json/to_string.h>
20#include <xrpl/ledger/OpenView.h>
21#include <xrpl/ledger/helpers/LendingHelpers.h>
22#include <xrpl/protocol/Feature.h>
23#include <xrpl/protocol/Indexes.h>
24#include <xrpl/protocol/Issue.h>
25#include <xrpl/protocol/Keylet.h>
26#include <xrpl/protocol/LedgerFormats.h>
27#include <xrpl/protocol/Protocol.h>
28#include <xrpl/protocol/SField.h>
29#include <xrpl/protocol/STAmount.h>
30#include <xrpl/protocol/SeqProxy.h>
31#include <xrpl/protocol/TER.h>
32#include <xrpl/protocol/TxFlags.h>
33#include <xrpl/protocol/Units.h>
34#include <xrpl/protocol/jss.h>
59 auto const sle = view.
read(loanKeylet);
63 (*replacement)[sfNextPaymentDueDate] = dueDate;
87 testcase(
"PoC: Unsigned-underflow full-pay accrual after early periodic");
91 using namespace std::chrono_literals;
93 Env env{*
this, features};
95 Account const lender{
"poc_lender4"};
96 Account const borrower{
"poc_borrower4"};
97 env.
fund(
XRP(3'000'000), lender, borrower);
106 auto const loanSetFee =
Fee(env.
current()->fees().base * 2);
107 Number const principalRequest = asset(1000).value();
108 auto const originationFee = asset(0).value();
109 auto const serviceFee = asset(1).value();
110 auto const serviceFeePA = asset(1);
111 auto const lateFee = asset(0).value();
112 auto const closeFee = asset(0).value();
117 auto const total = 3u;
118 auto const interval = 600u;
119 auto const grace = 60u;
121 auto createJtx = env.
jt(
122 set(borrower, broker.brokerID, principalRequest, 0),
123 Sig(sfCounterpartySignature, lender),
124 kLoanOriginationFee(originationFee),
125 kLoanServiceFee(serviceFee),
126 kLatePaymentFee(lateFee),
127 kClosePaymentFee(closeFee),
129 kInterestRate(interest),
130 kLateInterestRate(lateInterest),
131 kCloseInterestRate(closeInterest),
132 kOverpaymentInterestRate(overpaymentInterest),
133 kPaymentTotal(total),
134 kPaymentInterval(interval),
139 BEAST_EXPECT(brokerSle);
140 auto const loanSequence = brokerSle ? brokerSle->at(sfLoanSequence) : 0;
154 state.principalOutstanding,
155 state.managementFeeOutstanding,
156 state.periodicPayment,
158 state.paymentRemaining,
160 STAmount const regularDue{asset, components.trackedValueDelta + serviceFeePA.number()};
162 env(
pay(borrower, loanKeylet.key, regularDue));
168 auto const loanSle = env.
le(loanKeylet);
169 BEAST_EXPECT(loanSle);
171 BEAST_EXPECT(brokerSle2);
173 auto const closePaymentFee = loanSle ? loanSle->at(sfClosePaymentFee) :
Number{};
174 auto const closeInterestRate =
176 auto const managementFeeRate =
185 after.periodicPayment,
187 after.paymentRemaining),
189 env.
current()->parentCloseTime(),
190 after.paymentInterval,
191 after.previousPaymentDate,
196 auto const roundedInterest =
198 Number const roundedFullMgmtFee =
200 Number const roundedFullInterest = roundedInterest - roundedFullMgmtFee;
205 auto const startSecs =
207 auto const lastPaymentDate =
std::max(
after.previousPaymentDate, startSecs);
208 auto const signedDelta =
210 auto const unsignedDelta =
static_cast<std::uint32_t>(nowSecs - lastPaymentDate);
211 log <<
"PoC window: prev=" <<
after.previousPaymentDate <<
" start=" << startSecs
212 <<
" now=" << nowSecs <<
" signedDelta=" << signedDelta
213 <<
" unsignedDelta=" << unsignedDelta <<
std::endl;
217 auto const prevClamped =
std::min(
after.previousPaymentDate, nowSecs);
221 after.periodicPayment,
223 after.paymentRemaining),
225 env.
current()->parentCloseTime(),
226 after.paymentInterval,
230 auto const roundedInterestClamped =
233 asset.
raw(), roundedInterestClamped, managementFeeRate,
after.loanScale);
234 Number const roundedFullInterestClamped =
235 roundedInterestClamped - roundedFullMgmtFeeClamped;
238 after.principalOutstanding + roundedFullInterestClamped + roundedFullMgmtFeeClamped +
242 auto const vaultId2 = brokerSle2 ? brokerSle2->at(sfVaultID) :
UInt256{};
244 auto const vaultBefore = env.
le(vaultKey2);
245 BEAST_EXPECT(vaultBefore);
246 Number const assetsTotalBefore = vaultBefore ? vaultBefore->at(sfAssetsTotal) :
Number{};
250 after.principalOutstanding + roundedFullInterest + roundedFullMgmtFee +
253 log <<
"PoC payoff: principalOutstanding=" <<
after.principalOutstanding
254 <<
" roundedFullInterest=" << roundedFullInterest
255 <<
" roundedFullMgmtFee=" << roundedFullMgmtFee <<
" closeFee=" << closePaymentFee
257 log <<
"PoC reference (clamped): roundedFullInterestClamped=" << roundedFullInterestClamped
258 <<
" roundedFullMgmtFeeClamped=" << roundedFullMgmtFeeClamped
261 env(
pay(borrower, loanKeylet.key, fullDue),
Txflags(tfLoanFullPayment));
266 BEAST_EXPECT(unsignedDelta >
after.paymentInterval);
269 auto const vaultAfter = env.
le(vaultKey2);
270 BEAST_EXPECT(vaultAfter);
273 auto const assetsTotalAfter = vaultAfter->at(sfAssetsTotal);
274 log <<
"PoC NAV: assetsTotalBefore=" << assetsTotalBefore
275 <<
" assetsTotalAfter=" << assetsTotalAfter
276 <<
" delta=" << (assetsTotalAfter - assetsTotalBefore) <<
std::endl;
280 BEAST_EXPECT(fullDue == fullDueClamped);
281 if (fullDue != fullDueClamped)
282 log <<
"PoC delta: overcharge (fullDue > clamped)" <<
std::endl;
286 auto const finalLoan = env.
le(loanKeylet);
287 BEAST_EXPECT(finalLoan);
290 BEAST_EXPECT(finalLoan->at(sfPaymentRemaining) == 0);
291 BEAST_EXPECT(finalLoan->at(sfPrincipalOutstanding) == 0);
302 Account const issuer(
"issuer");
303 Account const lender(
"lender");
304 Account const borrower(
"borrower");
307 .vaultDeposit = 100000,
315 .principalRequest =
Number{200'000, -6},
316 .lateFee =
Number{200, -6},
323 Env env{*
this, features};
326 createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower);
328 if (BEAST_EXPECT(loanResult); !loanResult.has_value())
331 auto broker = std::get<BrokerInfo>(*loanResult);
332 auto loanKeylet = std::get<Keylet>(*loanResult);
338 if (
auto loan = env.
le(loanKeylet); BEAST_EXPECT(
loan))
340 env.
close(Tp{D{
loan->at(sfNextPaymentDueDate) +
loan->at(sfGracePeriod) + 1}});
351 auto const jr = env.
rpc(
"submit", borrower.
name(), submitParam);
353 BEAST_EXPECT(jr.isMember(jss::result));
370 testcase(
"RIPD-3459 - LoanBroker incorrect debt total");
374 Account const issuer(
"issuer");
375 Account const lender(
"lender");
376 Account const borrower(
"borrower");
379 .vaultDeposit = 200'000,
387 .principalRequest =
Number{100'000, -4},
393 Env env{*
this, features};
396 createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower);
398 if (BEAST_EXPECT(loanResult); !loanResult.has_value())
401 auto broker = std::get<BrokerInfo>(*loanResult);
402 auto loanKeylet = std::get<Keylet>(*loanResult);
403 auto pseudoAcct = std::get<Account>(*loanResult);
405 VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet);
407 if (
auto const brokerSle = env.
le(broker.brokerKeylet()); BEAST_EXPECT(brokerSle))
409 if (
auto const loanSle = env.
le(loanKeylet); BEAST_EXPECT(loanSle))
411 BEAST_EXPECT(brokerSle->at(sfDebtTotal) == loanSle->at(sfTotalValueOutstanding));
426 if (
auto const brokerSle = env.
le(broker.brokerKeylet()); BEAST_EXPECT(brokerSle))
428 if (
auto const loanSle = env.
le(loanKeylet); BEAST_EXPECT(loanSle))
430 BEAST_EXPECT(brokerSle->at(sfDebtTotal) == loanSle->at(sfTotalValueOutstanding));
431 BEAST_EXPECT(brokerSle->at(sfDebtTotal) ==
beast::kZero);
439 testcase(
"Crash with tfLoanOverpayment");
441 using namespace loan;
442 Account const lender{
"lender"};
443 Account const issuer{
"issuer"};
444 Account const borrower{
"borrower"};
445 Account const depositor{
"depositor"};
452 Vault const vault(env);
454 env.
fund(
XRP(10'000), lender, issuer, borrower, depositor);
457 auto [tx, vaultKeyLet, subscriptionDate] =
458 vault.createClosedEnded({.owner = lender, .asset =
xrpIssue()});
462 env(vault.deposit({.depositor = depositor, .id = vaultKeyLet.key, .amount = XRP(1'000)}),
468 vault.closePastSubscription(subscriptionDate);
470 auto const brokerKeyLet =
478 env(
set(borrower, brokerKeyLet.key, debtMaximumRequest),
479 Sig(sfCounterpartySignature, lender),
482 kPaymentInterval(150),
502 testcase(
"RIPD-3902 - 1 IOU loan payments");
506 Account const issuer(
"issuer");
507 Account const lender(
"lender");
508 Account const borrower(
"borrower");
519 .principalRequest =
Number{1, 0},
527 Env env{*
this, features};
530 createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower);
532 if (BEAST_EXPECT(loanResult); !loanResult.has_value())
535 auto broker = std::get<BrokerInfo>(*loanResult);
536 auto loanKeylet = std::get<Keylet>(*loanResult);
537 auto pseudoAcct = std::get<Account>(*loanResult);
539 VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet);
561 using namespace loan;
562 using namespace std::chrono_literals;
564 testcase(
"Impairment does not change payment due date");
566 Env env(*
this,
all_ | fixCleanup3_4_0);
567 BEAST_EXPECT(env.
enabled(fixCleanup3_4_0));
569 Account const lender{
"lender"};
570 Account const borrower{
"borrower"};
572 env.
fund(
XRP(100'000'000), lender, borrower);
579 if (!BEAST_EXPECT(sleBroker))
581 auto const loanKeylet =
584 Number const principalRequest{1, 3};
585 env(
set(borrower, broker.brokerID, broker.asset(principalRequest).value()),
586 Sig(sfCounterpartySignature, lender),
588 kPaymentInterval(600),
592 auto const loanSle = env.
le(loanKeylet);
593 if (!BEAST_EXPECT(loanSle))
595 std::uint32_t const originalNextDueDate = loanSle->at(sfNextPaymentDueDate);
596 BEAST_EXPECT(originalNextDueDate > 0);
599 env(manage(lender, loanKeylet.key, tfLoanImpair),
Ter(
tecTOO_SOON));
602 auto const loan = env.
le(loanKeylet);
603 BEAST_EXPECT(
loan->at(sfNextPaymentDueDate) == originalNextDueDate);
613 env.
current()->parentCloseTime().time_since_epoch().count();
616 env(manage(lender, loanKeylet.key, tfLoanImpair),
Ter(
tecTOO_SOON));
622 auto const loan = env.
le(loanKeylet);
623 BEAST_EXPECT(
loan->at(sfNextPaymentDueDate) == originalNextDueDate);
629 env(manage(lender, loanKeylet.key, tfLoanImpair),
Ter(
tesSUCCESS));
632 auto const loan = env.
le(loanKeylet);
633 if (!BEAST_EXPECT(
loan))
635 BEAST_EXPECT(
loan->isFlag(lsfLoanImpaired));
636 BEAST_EXPECT(
loan->at(sfNextPaymentDueDate) == originalNextDueDate);
640 env(manage(lender, loanKeylet.key, tfLoanUnimpair),
Ter(
tesSUCCESS));
643 auto const loan = env.
le(loanKeylet);
644 if (!BEAST_EXPECT(
loan))
646 BEAST_EXPECT(!
loan->isFlag(lsfLoanImpaired));
647 BEAST_EXPECT(
loan->at(sfNextPaymentDueDate) == originalNextDueDate);
663 using namespace loan;
664 using namespace std::chrono_literals;
666 testcase(
"Pre-amendment impair/unimpair date restoration");
668 Env env(*
this,
all_ - fixCleanup3_4_0);
669 BEAST_EXPECT(!env.
enabled(fixCleanup3_4_0));
671 Account const lender{
"lender"};
672 Account const borrower{
"borrower"};
674 env.
fund(
XRP(100'000'000), lender, borrower);
680 Number const principalRequest{1, 3};
681 auto createNewLoan = [&]() {
683 if (!BEAST_EXPECT(sleBroker))
687 env(
set(borrower, broker.brokerID, broker.asset(principalRequest).value()),
688 Sig(sfCounterpartySignature, lender),
690 kPaymentInterval(600),
704 if (!BEAST_EXPECT(brokerSle))
706 auto const coverNeeded =
707 broker.asset(broker.params.coverDeposit).value() - brokerSle->at(sfCoverAvailable);
711 lender, broker.brokerID,
STAmount{broker.asset, coverNeeded}));
714 env(del(lender, loanKeylet.
key));
720 auto const loanKeylet = createNewLoan();
721 auto const loanSle = env.
le(loanKeylet);
722 if (!BEAST_EXPECT(loanSle))
725 std::uint32_t const originalNextDueDate = loanSle->at(sfNextPaymentDueDate);
726 BEAST_EXPECT(originalNextDueDate == startDate + 600);
733 auto const loan = env.
le(loanKeylet);
734 if (!BEAST_EXPECT(
loan))
736 BEAST_EXPECT(
loan->isFlag(lsfLoanImpaired));
738 BEAST_EXPECT(movedDueDate != originalNextDueDate);
739 BEAST_EXPECT(movedDueDate < originalNextDueDate);
748 auto const loan = env.
le(loanKeylet);
749 if (!BEAST_EXPECT(
loan))
751 BEAST_EXPECT(!
loan->isFlag(lsfLoanImpaired));
752 BEAST_EXPECT(
loan->at(sfNextPaymentDueDate) == originalNextDueDate);
755 cleanupLoan(loanKeylet, originalNextDueDate);
760 auto const loanKeylet = createNewLoan();
761 auto const loanSle = env.
le(loanKeylet);
762 if (!BEAST_EXPECT(loanSle))
765 std::uint32_t const originalNextDueDate = loanSle->at(sfNextPaymentDueDate);
766 BEAST_EXPECT(originalNextDueDate == startDate + 600);
772 auto const timeBeforeUnimpair =
773 env.
current()->header().parentCloseTime.time_since_epoch().count();
778 auto const loan = env.
le(loanKeylet);
779 if (!BEAST_EXPECT(
loan))
781 BEAST_EXPECT(!
loan->isFlag(lsfLoanImpaired));
783 BEAST_EXPECT(newDueDate > originalNextDueDate);
784 BEAST_EXPECT(newDueDate == timeBeforeUnimpair + 600);
799 using namespace loan;
800 using namespace std::chrono_literals;
802 testcase(
"Impaired overdue LoanPay requires late-payment flag");
804 Env env(*
this,
all_ | fixCleanup3_4_0);
805 BEAST_EXPECT(env.
enabled(fixCleanup3_4_0));
807 Account const lender{
"lender"};
808 Account const borrower{
"borrower"};
810 env.
fund(
XRP(100'000'000), lender, borrower);
817 if (!BEAST_EXPECT(sleBroker))
819 auto const loanKeylet =
824 Number const principalRequest{1, 3};
825 env(
set(borrower, broker.brokerID, broker.asset(principalRequest).value()),
826 Sig(sfCounterpartySignature, lender),
828 kPaymentInterval(600),
829 kLatePaymentFee(broker.asset(3).number()),
834 auto const loanSle = env.
le(loanKeylet);
835 if (!BEAST_EXPECT(loanSle))
837 std::uint32_t const originalNextDueDate = loanSle->at(sfNextPaymentDueDate);
838 std::uint32_t const paymentsBefore = loanSle->at(sfPaymentRemaining);
839 BEAST_EXPECT(originalNextDueDate > 0);
844 env(manage(lender, loanKeylet.key, tfLoanImpair),
Ter(
tesSUCCESS));
848 auto const loan = env.
le(loanKeylet);
849 if (!BEAST_EXPECT(
loan))
851 BEAST_EXPECT(
loan->isFlag(lsfLoanImpaired));
852 BEAST_EXPECT(
loan->at(sfNextPaymentDueDate) == originalNextDueDate);
855 auto const payAmount = broker.asset(500).value();
865 auto const loan = env.
le(loanKeylet);
866 if (!BEAST_EXPECT(
loan))
868 BEAST_EXPECT(
loan->isFlag(lsfLoanImpaired));
869 BEAST_EXPECT(
loan->at(sfPaymentRemaining) == paymentsBefore);
870 BEAST_EXPECT(
loan->at(sfNextPaymentDueDate) == originalNextDueDate);
873 env(
pay(borrower, loanKeylet.key, payAmount, tfLoanLatePayment),
Ter(
tesSUCCESS));
876 auto const loan = env.
le(loanKeylet);
877 if (!BEAST_EXPECT(
loan))
879 BEAST_EXPECT(!
loan->isFlag(lsfLoanImpaired));
880 BEAST_EXPECT(
loan->at(sfPaymentRemaining) == paymentsBefore - 1);
884 auto const vaultSle = env.
le(broker.vaultKeylet());
885 if (!BEAST_EXPECT(vaultSle))
887 BEAST_EXPECT(vaultSle->at(sfLossUnrealized) == 0);
902 using namespace loan;
903 using namespace std::chrono_literals;
905 testcase(
"Impaired overdue LoanPay bypass (pre-amendment)");
907 Env env(*
this,
all_ - fixCleanup3_4_0);
908 BEAST_EXPECT(!env.
enabled(fixCleanup3_4_0));
910 Account const lender{
"lender"};
911 Account const borrower{
"borrower"};
913 env.
fund(
XRP(100'000'000), lender, borrower);
920 if (!BEAST_EXPECT(sleBroker))
922 auto const loanKeylet =
925 Number const principalRequest{1, 3};
926 env(
set(borrower, broker.brokerID, broker.asset(principalRequest).value()),
927 Sig(sfCounterpartySignature, lender),
929 kPaymentInterval(600),
930 kLatePaymentFee(broker.asset(3).number()),
935 auto const loanSle = env.
le(loanKeylet);
936 if (!BEAST_EXPECT(loanSle))
938 std::uint32_t const originalNextDueDate = loanSle->at(sfNextPaymentDueDate);
939 BEAST_EXPECT(originalNextDueDate > 0);
941 env(manage(lender, loanKeylet.key, tfLoanImpair),
Ter(
tesSUCCESS));
947 auto const loan = env.
le(loanKeylet);
948 if (!BEAST_EXPECT(
loan))
950 BEAST_EXPECT(
loan->isFlag(lsfLoanImpaired));
953 auto const payAmount = broker.asset(500).value();
961 auto const loan = env.
le(loanKeylet);
962 if (!BEAST_EXPECT(
loan))
964 BEAST_EXPECT(!
loan->isFlag(lsfLoanImpaired));
975 testcase(
"LoanManage default at exact grace expiry rejected with fixCleanup3_4_0");
978 using namespace loan;
979 using namespace std::chrono_literals;
982 BEAST_EXPECT(env.
enabled(fixCleanup3_4_0));
984 Account const lender{
"lender"};
985 Account const borrower{
"borrower"};
987 env.
fund(
XRP(100'000'000), lender, borrower);
994 if (!BEAST_EXPECT(sleBroker))
996 auto const loanKeylet =
999 env(
set(borrower, broker.brokerID, broker.asset(
Number{1, 3}).value()),
1000 Sig(sfCounterpartySignature, lender),
1002 kPaymentInterval(600),
1011 auto const loanSle = env.
le(loanKeylet);
1012 if (!BEAST_EXPECT(loanSle))
1014 auto const grace = loanSle->at(sfGracePeriod);
1016 BEAST_EXPECT(now > grace + 1);
1021 env(manage(lender, loanKeylet.key, tfLoanDefault),
Ter(
tecTOO_SOON));
1023 auto const loan = env.
le(loanKeylet);
1024 if (!BEAST_EXPECT(
loan))
1026 BEAST_EXPECT(!
loan->isFlag(lsfLoanDefault));
1031 env(manage(lender, loanKeylet.key, tfLoanDefault),
Ter(
tesSUCCESS));
1033 auto const loan = env.
le(loanKeylet);
1034 if (!BEAST_EXPECT(
loan))
1036 BEAST_EXPECT(
loan->isFlag(lsfLoanDefault));
1043 testcase(
"LoanManage default at exact grace expiry succeeds without fixCleanup3_4_0");
1045 using namespace jtx;
1046 using namespace loan;
1047 using namespace std::chrono_literals;
1049 Env env(*
this,
all_ - fixCleanup3_4_0);
1050 BEAST_EXPECT(!env.
enabled(fixCleanup3_4_0));
1052 Account const lender{
"lender"};
1053 Account const borrower{
"borrower"};
1055 env.
fund(
XRP(100'000'000), lender, borrower);
1062 if (!BEAST_EXPECT(sleBroker))
1064 auto const loanKeylet =
1067 env(
set(borrower, broker.brokerID, broker.asset(
Number{1, 3}).value()),
1068 Sig(sfCounterpartySignature, lender),
1070 kPaymentInterval(600),
1077 auto const loanSle = env.
le(loanKeylet);
1078 if (!BEAST_EXPECT(loanSle))
1080 auto const grace = loanSle->at(sfGracePeriod);
1082 BEAST_EXPECT(now > grace);
1085 env(manage(lender, loanKeylet.key, tfLoanDefault),
Ter(
tesSUCCESS));
1087 auto const loan = env.
le(loanKeylet);
1088 if (!BEAST_EXPECT(
loan))
1090 BEAST_EXPECT(
loan->isFlag(lsfLoanDefault));
1104 std::string(
"Counterparty signature copied into the sponsor slot") +
1105 (fixEnabled ?
"" :
" (pre-amendment)"));
1107 using namespace jtx;
1108 using namespace loan;
1110 Env env(*
this, fixEnabled ?
all_ :
all_ - fixCleanup3_4_0);
1111 BEAST_EXPECT(env.
enabled(fixCleanup3_4_0) == fixEnabled);
1113 Account const lender{
"lender"};
1114 Account const borrower{
"borrower"};
1116 env.
fund(
XRP(100'000'000), lender, borrower);
1122 auto const feeAmt =
XRP(1);
1127 auto loanSet = env.
json(
1128 set(borrower, broker.brokerID, broker.asset(
Number{1, 3}).value()),
1130 Sig(sfCounterpartySignature, lender),
1134 loanSet[sfSponsorSignature.jsonName] = loanSet[sfCounterpartySignature.jsonName];
1136 auto const lenderBalance = env.
balance(lender);
1137 auto const borrowerBalance = env.
balance(borrower);
1146 BEAST_EXPECT(env.
balance(lender) == lenderBalance);
1147 BEAST_EXPECT(env.
balance(borrower) == borrowerBalance);
1152 BEAST_EXPECT(env.
balance(lender) == lenderBalance - feeAmt);
1187 {fixCleanup3_1_3, fixCleanup3_2_0, featureMPTokensV2},
all_))
A generic endpoint for log messages.
LogOs< char > log
Logging output stream.
TestcaseT testcase
Memberspace for declaring test cases.
std::chrono::time_point< NetClock > time_point
std::chrono::duration< rep, period > duration
Number is a floating point type that can represent a wide range of values.
bool modify(ModifyType const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
void rawReplace(SLE::Ref sle) override
Unconditionally replace a state item.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
STAmount const & value() const noexcept
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
virtual OpenLedger & getOpenLedger()=0
void testImpairmentPaymentDateUnchanged()
void testRIPD3902(FeatureBitset features)
void runAmendmentSensitive(FeatureBitset features)
void testLoanDefaultAtExactGraceExpiryRejectedPostAmendment()
void setLoanNextPaymentDueDate(jtx::Env &env, Keylet const &loanKeylet, std::uint32_t dueDate)
void testSignatureCopiedBetweenRoles(bool fixEnabled)
void testRIPD3459(FeatureBitset features)
void runAmendmentIndependent()
void run() override
Runs the suite.
void testPoCUnsignedUnderflowOnFullPayAfterEarlyPeriodic(FeatureBitset features)
void testImpairedOverdueLoanPayBypassPreAmendment()
void testImpairedOverdueLoanPayRequiresLateFlag()
void testLoanDefaultAtExactGraceExpirySucceedsPreAmendment()
void testImpairmentPaymentDatePreAmendment()
void testRIPD3831(FeatureBitset features)
Shared base for the Loan*_test family under src/test/app/lending/.
BrokerInfo createVaultAndBroker(jtx::Env &env, jtx::PrettyAsset const &asset, jtx::Account const &lender, BrokerParameters const ¶ms=BrokerParameters::defaults())
LoanState getCurrentState(jtx::Env const &env, BrokerInfo const &broker, Keylet const &loanKeylet)
Get the state without checking anything.
void makeLoanPayments(jtx::Env &env, BrokerInfo const &broker, LoanParameters const &loanParams, Keylet const &loanKeylet, VerifyLoanStatus const &verifyLoanStatus, jtx::Account const &issuer, jtx::Account const &lender, jtx::Account const &borrower, PaymentParameters const &paymentParams=PaymentParameters::defaults())
std::optional< std::tuple< BrokerInfo, Keylet, jtx::Account > > createLoan(jtx::Env &env, AssetType assetType, BrokerParameters const &brokerParams, LoanParameters const &loanParams, jtx::Account const &issuer, jtx::Account const &lender, jtx::Account const &borrower)
static void topUpBorrower(jtx::Env &env, BrokerInfo const &broker, jtx::Account const &issuer, jtx::Account const &borrower, LoanState const &state, std::optional< Number > const &servFee)
Immutable cryptographic account descriptor.
std::string const & name() const
Return the name.
AccountID id() const
Returns the Account ID.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
json::Value json(JsonValue &&jv, FN const &... fN)
Create JSON from parameters.
SLE::const_pointer le(Account const &account) const
Return an account root.
bool enabled(UInt256 feature) const
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
beast::unit_test::Suite & test
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
NetClock::time_point now()
Returns the current network time.
Set the regular signature on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Number loanPrincipalFromPeriodicPayment(Rules const &rules, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentsRemaining)
PaymentComponents computePaymentComponents(Rules const &rules, Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet loanBroker(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet loan(UInt256 const &loanBrokerID, SeqProxy const &loanSeq) noexcept
json::Value set(AccountID const &account, UInt256 const &vaultId, uint32_t flags)
json::Value coverDeposit(AccountID const &account, UInt256 const &brokerID, STAmount const &amount, uint32_t flags)
json::Value pay(AccountID const &account, UInt256 const &loanID, STAmount const &amount, std::uint32_t flags)
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::vector< FeatureBitset > amendmentCombinations(std::initializer_list< UInt256 > features, FeatureBitset seed)
Returns all 2^N permutations of a seed FeatureBitset with each subset of the given features excluded.
XrpT const XRP
Converts to XRP Issue or STAmount.
json::Value noop(Account const &account)
The null transaction.
XRPAmount txFee(Env const &env, std::uint16_t n)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
constexpr XRPAmount
Convert XRP to drops (integral types).
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr FlagValue spfSponsorFee
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
constexpr TenthBips32 percentageToTenthBips(std::uint32_t percentage)
TenthBips< std::uint32_t > TenthBips32
TenthBips< std::uint16_t > TenthBips16
std::string to_string(BaseUInt< Bits, Tag > const &a)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Number computeManagementFee(Asset const &asset, Number const &interest, TenthBips32 managementFeeRate, std::int32_t scale)
TERSubset< CanCvtToTER > TER
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
A pair of SHAMap key and LedgerEntryType.
TenthBips16 managementFeeRate
std::optional< Number > serviceFee
Helper class to compare the expected state of a loan and loan broker against the data in the ledger.
STAmount const & value() const
Asset const & raw() const