51 Account const borrower{
"borrower"};
56 bool authorizeBorrower =
false;
57 int initialXRP = 1'000'000;
60 auto const testCase = [&,
this](
64 Env env(*
this, features);
65 env.
fund(
XRP(args.initialXRP), issuer, lender, borrower);
69 env(
fset(issuer, asfRequireAuth));
79 {.flags = tfMPTCanTransfer | tfMPTCanLock |
80 (args.requireAuth ? tfMPTRequireAuth :
kNone)});
88 mptt.
authorize({.account = issuer, .holder = lender});
89 if (args.authorizeBorrower)
90 mptt.
authorize({.account = issuer, .holder = borrower});
94 env(
pay(issuer, lender, mptAsset(10'000'000)));
99 env(
trust(lender, iouAsset(10'000'000)));
100 env(
trust(borrower, iouAsset(10'000'000)));
102 if (args.requireAuth)
104 env(
trust(issuer, iouAsset(0), lender, tfSetfAuth));
105 env(
pay(issuer, lender, iouAsset(10'000'000)));
106 if (args.authorizeBorrower)
108 env(
trust(issuer, iouAsset(0), borrower, tfSetfAuth));
109 env(
pay(issuer, borrower, iouAsset(10'000)));
114 env(
pay(issuer, lender, iouAsset(10'000'000)));
115 env(
pay(issuer, borrower, iouAsset(10'000)));
123 for (
auto const& asset : assets)
129 mptTest(env, brokers[0], mptt);
131 iouTest(env, brokers[1]);
136 using namespace loan;
137 Number const principalRequest = broker.
asset(1'000).value();
139 testcase(
"MPT issuer is borrower, issuer submits");
140 env(
set(issuer, broker.
brokerID, principalRequest),
141 kCounterparty(lender),
142 Sig(sfCounterpartySignature, lender),
145 testcase(
"MPT issuer is borrower, lender submits");
146 env(
set(lender, broker.
brokerID, principalRequest),
147 kCounterparty(issuer),
148 Sig(sfCounterpartySignature, issuer),
152 using namespace loan;
153 Number const principalRequest = broker.
asset(1'000).value();
155 testcase(
"IOU issuer is borrower, issuer submits");
156 env(
set(issuer, broker.
brokerID, principalRequest),
157 kCounterparty(lender),
158 Sig(sfCounterpartySignature, lender),
161 testcase(
"IOU issuer is borrower, lender submits");
162 env(
set(lender, broker.
brokerID, principalRequest),
163 kCounterparty(issuer),
164 Sig(sfCounterpartySignature, issuer),
167 CaseArgs{.requireAuth =
true});
171 using namespace loan;
172 Number const principalRequest = broker.
asset(1'000).value();
174 testcase(
"MPT unauthorized borrower, borrower submits");
175 env(
set(borrower, broker.
brokerID, principalRequest),
176 kCounterparty(lender),
177 Sig(sfCounterpartySignature, lender),
181 testcase(
"MPT unauthorized borrower, lender submits");
182 env(
set(lender, broker.
brokerID, principalRequest),
183 kCounterparty(borrower),
184 Sig(sfCounterpartySignature, borrower),
189 using namespace loan;
190 Number const principalRequest = broker.
asset(1'000).value();
192 testcase(
"IOU unauthorized borrower, borrower submits");
193 env(
set(borrower, broker.
brokerID, principalRequest),
194 kCounterparty(lender),
195 Sig(sfCounterpartySignature, lender),
199 testcase(
"IOU unauthorized borrower, lender submits");
200 env(
set(lender, broker.
brokerID, principalRequest),
201 kCounterparty(borrower),
202 Sig(sfCounterpartySignature, borrower),
206 CaseArgs{.requireAuth =
true});
217 using namespace loan;
218 Number const principalRequest = broker.
asset(1'000).value();
221 "MPT authorized borrower, borrower submits, borrower has "
223 mptt.
authorize({.account = borrower, .flags = tfMPTUnauthorize});
227 auto const sleMPT1 = env.
le(mptoken);
228 BEAST_EXPECT(sleMPT1 ==
nullptr);
231 env(
noop(borrower),
Fee(
XRP((acctReserve * 2) + (incReserve * 2))));
235 env(
set(borrower, broker.
brokerID, principalRequest),
236 kCounterparty(lender),
237 Sig(sfCounterpartySignature, lender),
239 Ter{tecINSUFFICIENT_RESERVE});
243 env(
pay(issuer, borrower,
XRP(incReserve)));
245 env(
set(borrower, broker.
brokerID, principalRequest),
246 kCounterparty(lender),
247 Sig(sfCounterpartySignature, lender),
251 auto const sleMPT2 = env.
le(mptoken);
252 BEAST_EXPECT(sleMPT2 !=
nullptr);
255 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
260 using namespace loan;
261 Number const principalRequest = broker.
asset(1'000).value();
264 "IOU authorized borrower, borrower submits, borrower has "
270 env(
pay(borrower, issuer, broker.
asset(10'000)));
273 auto const sleLine1 = env.
le(trustline);
274 BEAST_EXPECT(sleLine1 ==
nullptr);
277 env(
noop(borrower),
Fee(
XRP((acctReserve * 2) + (incReserve * 2))));
281 env(
set(borrower, broker.
brokerID, principalRequest),
282 kCounterparty(lender),
283 Sig(sfCounterpartySignature, lender),
285 Ter{tecNO_LINE_INSUF_RESERVE});
289 env(
pay(issuer, borrower,
XRP(incReserve)));
291 env(
set(borrower, broker.
brokerID, principalRequest),
292 kCounterparty(lender),
293 Sig(sfCounterpartySignature, lender),
297 auto const sleLine2 = env.
le(trustline);
298 BEAST_EXPECT(sleLine2 !=
nullptr);
300 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
304 using namespace loan;
305 Number const principalRequest = broker.
asset(1'000).value();
308 "MPT authorized borrower, borrower submits, lender has "
311 auto const sleMPT1 = env.
le(mptoken);
312 BEAST_EXPECT(sleMPT1 !=
nullptr);
314 env(
pay(lender, issuer, broker.
asset(sleMPT1->at(sfMPTAmount))));
317 mptt.
authorize({.account = lender, .flags = tfMPTUnauthorize});
320 auto const sleMPT2 = env.
le(mptoken);
321 BEAST_EXPECT(sleMPT2 ==
nullptr);
328 env(
set(borrower, broker.
brokerID, principalRequest),
329 kLoanOriginationFee(broker.
asset(1).value()),
330 kCounterparty(lender),
331 Sig(sfCounterpartySignature, lender),
333 Ter{tecINSUFFICIENT_RESERVE});
337 env(
pay(issuer, lender,
XRP(incReserve)));
339 env(
set(borrower, broker.
brokerID, principalRequest),
340 kLoanOriginationFee(broker.
asset(1).value()),
341 kCounterparty(lender),
342 Sig(sfCounterpartySignature, lender),
346 auto const sleMPT3 = env.
le(mptoken);
347 BEAST_EXPECT(sleMPT3 !=
nullptr);
350 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
355 using namespace loan;
356 Number const principalRequest = broker.
asset(1'000).value();
359 "IOU authorized borrower, borrower submits, lender has no "
366 auto const sleLine1 = env.
le(trustline);
367 BEAST_EXPECT(sleLine1 !=
nullptr);
369 env(
pay(lender, issuer, broker.
asset(
abs(sleLine1->at(sfBalance).value()))));
371 auto const sleLine2 = env.
le(trustline);
372 BEAST_EXPECT(sleLine2 ==
nullptr);
379 env(
set(borrower, broker.
brokerID, principalRequest),
380 kLoanOriginationFee(broker.
asset(1).value()),
381 kCounterparty(lender),
382 Sig(sfCounterpartySignature, lender),
384 Ter{tecNO_LINE_INSUF_RESERVE});
388 env(
pay(issuer, lender,
XRP(incReserve)));
390 env(
set(borrower, broker.
brokerID, principalRequest),
391 kLoanOriginationFee(broker.
asset(1).value()),
392 kCounterparty(lender),
393 Sig(sfCounterpartySignature, lender),
397 auto const sleLine3 = env.
le(trustline);
398 BEAST_EXPECT(sleLine3 !=
nullptr);
400 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
404 using namespace loan;
405 Number const principalRequest = broker.
asset(1'000).value();
407 testcase(
"MPT authorized borrower, unauthorized lender");
409 auto const sleMPT1 = env.
le(mptoken);
410 BEAST_EXPECT(sleMPT1 !=
nullptr);
412 env(
pay(lender, issuer, broker.
asset(sleMPT1->at(sfMPTAmount))));
415 mptt.
authorize({.account = lender, .flags = tfMPTUnauthorize});
418 auto const sleMPT2 = env.
le(mptoken);
419 BEAST_EXPECT(sleMPT2 ==
nullptr);
422 env(
set(borrower, broker.
brokerID, principalRequest),
423 kLoanOriginationFee(broker.
asset(1).value()),
424 kCounterparty(lender),
425 Sig(sfCounterpartySignature, lender),
431 env(
set(borrower, broker.
brokerID, principalRequest),
432 kCounterparty(lender),
433 Sig(sfCounterpartySignature, lender),
439 auto const sleMPT3 = env.
le(mptoken);
440 BEAST_EXPECT(sleMPT3 ==
nullptr);
443 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
447 using namespace loan;
448 Number const principalRequest = broker.
asset(1'000).value();
450 testcase(
"MPT authorized borrower, borrower submits");
451 env(
set(borrower, broker.
brokerID, principalRequest),
452 kCounterparty(lender),
453 Sig(sfCounterpartySignature, lender),
457 using namespace loan;
458 Number const principalRequest = broker.
asset(1'000).value();
460 testcase(
"IOU authorized borrower, borrower submits");
461 env(
set(borrower, broker.
brokerID, principalRequest),
462 kCounterparty(lender),
463 Sig(sfCounterpartySignature, lender),
466 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
470 using namespace loan;
471 Number const principalRequest = broker.
asset(1'000).value();
473 testcase(
"MPT authorized borrower, lender submits");
474 env(
set(lender, broker.
brokerID, principalRequest),
475 kCounterparty(borrower),
476 Sig(sfCounterpartySignature, borrower),
480 using namespace loan;
481 Number const principalRequest = broker.
asset(1'000).value();
483 testcase(
"IOU authorized borrower, lender submits");
484 env(
set(lender, broker.
brokerID, principalRequest),
485 kCounterparty(borrower),
486 Sig(sfCounterpartySignature, borrower),
489 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
493 auto const msigSetup = [&](
Env& env,
Account const& account) {
501 using namespace loan;
502 msigSetup(env, lender);
503 Number const principalRequest = broker.
asset(1'000).value();
506 "MPT authorized borrower, borrower submits, lender "
508 env(
set(borrower, broker.
brokerID, principalRequest),
509 kCounterparty(lender),
510 Msig(sfCounterpartySignature, alice, bella),
514 using namespace loan;
515 msigSetup(env, lender);
516 Number const principalRequest = broker.
asset(1'000).value();
519 "IOU authorized borrower, borrower submits, lender "
521 env(
set(borrower, broker.
brokerID, principalRequest),
522 kCounterparty(lender),
523 Msig(sfCounterpartySignature, alice, bella),
526 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
530 using namespace loan;
531 msigSetup(env, borrower);
532 Number const principalRequest = broker.
asset(1'000).value();
535 "MPT authorized borrower, lender submits, borrower "
537 env(
set(lender, broker.
brokerID, principalRequest),
538 kCounterparty(borrower),
539 Msig(sfCounterpartySignature, alice, bella),
543 using namespace loan;
544 msigSetup(env, borrower);
545 Number const principalRequest = broker.
asset(1'000).value();
548 "IOU authorized borrower, lender submits, borrower "
550 env(
set(lender, broker.
brokerID, principalRequest),
551 kCounterparty(borrower),
552 Msig(sfCounterpartySignature, alice, bella),
555 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
559 using namespace loan;
560 Number const principalRequest = broker.
asset(1'000).value();
561 Vault const vault{env};
562 auto tx = vault.set({.owner = lender, .
id = broker.
vaultID});
563 tx[sfAssetsMaximum] = BrokerParameters::defaults().vaultDeposit;
568 env(
set(issuer, broker.
brokerID, principalRequest),
569 kCounterparty(lender),
571 Sig(sfCounterpartySignature, lender),
579 using namespace loan;
580 Number const principalRequest = broker.
asset(1'000).value();
581 Vault const vault{env};
582 auto tx = vault.set({.owner = lender, .
id = broker.
vaultID});
583 tx[sfAssetsMaximum] =
584 BrokerParameters::defaults().vaultDeposit + broker.
asset(1).number();
588 testcase(
"Vault maximum value exceeded");
589 env(
set(issuer, broker.
brokerID, principalRequest),
590 kCounterparty(lender),
592 Sig(sfCounterpartySignature, lender),
595 kPaymentInterval(3600 * 24),
605 using namespace loan;
607 bool const fix340Enabled = features[fixCleanup3_4_0];
608 testcase <<
"LoanSet: borrower and broker owner missing MPToken"
609 << (fix340Enabled ?
"" :
" pre-fixCleanup3_4_0");
611 Account const issuer{
"issuer"};
612 Account const lender{
"lender"};
613 Account const borrower{
"borrower"};
615 Env env(*
this, features);
616 env.
fund(
XRP(1'000'000), issuer, lender, borrower);
620 mptt.
create({.flags = tfMPTCanTransfer | tfMPTCanLock});
627 env(
pay(issuer, lender, asset(10'000'000)));
633 mptt.
authorize({.account = borrower, .flags = tfMPTUnauthorize});
638 auto const sleLenderMPT = env.
le(lenderMPToken);
639 if (!BEAST_EXPECT(sleLenderMPT))
641 env(
pay(lender, issuer, asset(sleLenderMPT->at(sfMPTAmount))));
643 mptt.
authorize({.account = lender, .flags = tfMPTUnauthorize});
648 auto const sleBrokerBefore = env.
le(brokerKeylet);
649 if (!BEAST_EXPECT(sleBrokerBefore))
651 auto const loanSequence = sleBrokerBefore->at(sfLoanSequence);
652 auto const debtTotalBefore = sleBrokerBefore->at(sfDebtTotal);
656 if (!BEAST_EXPECT(sleVaultBefore))
658 auto const assetsAvailableBefore = sleVaultBefore->at(sfAssetsAvailable);
660 env(
set(borrower, broker.brokerID, asset(1'000).value()),
661 kLoanOriginationFee(asset(1).value()),
662 kCounterparty(lender),
663 Sig(sfCounterpartySignature, lender),
665 Ter{fix340Enabled ? TER{tesSUCCESS} :
TER{tecINVARIANT_FAILED}});
668 auto const sleBorrowerAfter = env.le(borrowerMPToken);
669 auto const sleLenderAfter = env.le(lenderMPToken);
670 auto const sleLoanAfter = env.le(loanKeylet);
671 auto const sleBrokerAfter = env.le(brokerKeylet);
672 auto const sleVaultAfter = env.le(
keylet::vault(broker.vaultID));
673 if (!BEAST_EXPECT(sleVaultAfter))
677 if (!BEAST_EXPECT(sleBorrowerAfter && sleLenderAfter && sleLoanAfter && sleBrokerAfter))
679 BEAST_EXPECT(sleBorrowerAfter->at(sfMPTAmount) == 999);
680 BEAST_EXPECT(sleLenderAfter->at(sfMPTAmount) == 1);
681 BEAST_EXPECT(sleLoanAfter->at(sfPrincipalOutstanding) ==
Number{1'000});
682 BEAST_EXPECT(sleBrokerAfter->at(sfLoanSequence) == loanSequence + 1);
684 sleVaultAfter->at(sfAssetsAvailable) == assetsAvailableBefore -
Number{1'000});
689 BEAST_EXPECT(!sleBorrowerAfter);
690 BEAST_EXPECT(!sleLenderAfter);
691 BEAST_EXPECT(!sleLoanAfter);
692 if (!BEAST_EXPECT(sleBrokerAfter))
694 BEAST_EXPECT(sleBrokerAfter->at(sfLoanSequence) == loanSequence);
695 BEAST_EXPECT(sleBrokerAfter->at(sfDebtTotal) == debtTotalBefore);
696 BEAST_EXPECT(sleVaultAfter->at(sfAssetsAvailable) == assetsAvailableBefore);
704 testcase(
"LoanSet closed-ended: phase and maturity bound");
706 using namespace loan;
710 Account const issuer{
"issuer"};
711 Account const lender{
"lender"};
712 Account const borrower{
"borrower"};
721 auto const withEnv = [&,
this](
auto&& body) {
723 env.
fund(
XRP(1'000'000'000), issuer, lender, borrower);
729 auto const setLoan = [&](
Env& env,
BrokerInfo const& broker,
TER expected) {
730 env(
set(lender, broker.brokerID, broker.asset(100).value()),
731 kCounterparty(borrower),
732 Sig(sfCounterpartySignature, borrower),
734 kPaymentTotal(kTotal),
735 kPaymentInterval(kInterval),
756 BEAST_EXPECT(broker.redemptionDate.has_value());
759 env.
close(Tp{D{*broker.redemptionDate + 1}});
774 constexpr std::uint32_t kRedemptionOffset = 3u * 24u * 3600u;
781 env(
set(lender, broker.brokerID, broker.asset(100).value()),
782 kCounterparty(borrower),
783 Sig(sfCounterpartySignature, borrower),
786 kPaymentInterval(kInterval),
797 BEAST_EXPECT(broker.redemptionDate.has_value());
801 env(
set(lender, broker.brokerID, broker.asset(100).value()),
802 kCounterparty(borrower),
803 Sig(sfCounterpartySignature, borrower),
806 kPaymentInterval(acceptInterval),
812 env(
set(lender, broker.brokerID, broker.asset(100).value()),
813 kCounterparty(borrower),
814 Sig(sfCounterpartySignature, borrower),
817 kPaymentInterval(rejectInterval),
835 .subscriptionOffset = 300u,
837 .skipPhaseAdvance =
true});
838 BEAST_EXPECT(broker.subscriptionDate.has_value());
839 BEAST_EXPECT(broker.redemptionDate.has_value());
841 auto const red = *broker.redemptionDate;
843 auto const minLoan = [&](
TER expected) {
844 env(
set(lender, broker.brokerID, broker.asset(100).value()),
845 kCounterparty(borrower),
846 Sig(sfCounterpartySignature, borrower),
855 env.
close(Tp{D{*broker.subscriptionDate + 1}});
856 BEAST_EXPECT(startDate() > *broker.subscriptionDate);
863 BEAST_EXPECT(startDate() < red);