48 Account const borrower{
"borrower"};
53 bool authorizeBorrower =
false;
54 int initialXRP = 1'000'000;
57 auto const testCase = [&,
this](
61 Env env(*
this, features);
62 env.
fund(
XRP(args.initialXRP), issuer, lender, borrower);
66 env(
fset(issuer, asfRequireAuth));
76 {.flags = tfMPTCanTransfer | tfMPTCanLock |
77 (args.requireAuth ? tfMPTRequireAuth :
kNone)});
85 mptt.
authorize({.account = issuer, .holder = lender});
86 if (args.authorizeBorrower)
87 mptt.
authorize({.account = issuer, .holder = borrower});
91 env(
pay(issuer, lender, mptAsset(10'000'000)));
96 env(
trust(lender, iouAsset(10'000'000)));
97 env(
trust(borrower, iouAsset(10'000'000)));
101 env(
trust(issuer, iouAsset(0), lender, tfSetfAuth));
102 env(
pay(issuer, lender, iouAsset(10'000'000)));
103 if (args.authorizeBorrower)
105 env(
trust(issuer, iouAsset(0), borrower, tfSetfAuth));
106 env(
pay(issuer, borrower, iouAsset(10'000)));
111 env(
pay(issuer, lender, iouAsset(10'000'000)));
112 env(
pay(issuer, borrower, iouAsset(10'000)));
120 for (
auto const& asset : assets)
126 mptTest(env, brokers[0], mptt);
128 iouTest(env, brokers[1]);
133 using namespace loan;
134 Number const principalRequest = broker.
asset(1'000).value();
136 testcase(
"MPT issuer is borrower, issuer submits");
137 env(
set(issuer, broker.
brokerID, principalRequest),
138 kCounterparty(lender),
139 Sig(sfCounterpartySignature, lender),
142 testcase(
"MPT issuer is borrower, lender submits");
143 env(
set(lender, broker.
brokerID, principalRequest),
144 kCounterparty(issuer),
145 Sig(sfCounterpartySignature, issuer),
149 using namespace loan;
150 Number const principalRequest = broker.
asset(1'000).value();
152 testcase(
"IOU issuer is borrower, issuer submits");
153 env(
set(issuer, broker.
brokerID, principalRequest),
154 kCounterparty(lender),
155 Sig(sfCounterpartySignature, lender),
158 testcase(
"IOU issuer is borrower, lender submits");
159 env(
set(lender, broker.
brokerID, principalRequest),
160 kCounterparty(issuer),
161 Sig(sfCounterpartySignature, issuer),
164 CaseArgs{.requireAuth =
true});
168 using namespace loan;
169 Number const principalRequest = broker.
asset(1'000).value();
171 testcase(
"MPT unauthorized borrower, borrower submits");
172 env(
set(borrower, broker.
brokerID, principalRequest),
173 kCounterparty(lender),
174 Sig(sfCounterpartySignature, lender),
178 testcase(
"MPT unauthorized borrower, lender submits");
179 env(
set(lender, broker.
brokerID, principalRequest),
180 kCounterparty(borrower),
181 Sig(sfCounterpartySignature, borrower),
186 using namespace loan;
187 Number const principalRequest = broker.
asset(1'000).value();
189 testcase(
"IOU unauthorized borrower, borrower submits");
190 env(
set(borrower, broker.
brokerID, principalRequest),
191 kCounterparty(lender),
192 Sig(sfCounterpartySignature, lender),
196 testcase(
"IOU unauthorized borrower, lender submits");
197 env(
set(lender, broker.
brokerID, principalRequest),
198 kCounterparty(borrower),
199 Sig(sfCounterpartySignature, borrower),
203 CaseArgs{.requireAuth =
true});
214 using namespace loan;
215 Number const principalRequest = broker.
asset(1'000).value();
218 "MPT authorized borrower, borrower submits, borrower has "
220 mptt.
authorize({.account = borrower, .flags = tfMPTUnauthorize});
224 auto const sleMPT1 = env.
le(mptoken);
225 BEAST_EXPECT(sleMPT1 ==
nullptr);
228 env(
noop(borrower),
Fee(
XRP((acctReserve * 2) + (incReserve * 2))));
232 env(
set(borrower, broker.
brokerID, principalRequest),
233 kCounterparty(lender),
234 Sig(sfCounterpartySignature, lender),
236 Ter{tecINSUFFICIENT_RESERVE});
240 env(
pay(issuer, borrower,
XRP(incReserve)));
242 env(
set(borrower, broker.
brokerID, principalRequest),
243 kCounterparty(lender),
244 Sig(sfCounterpartySignature, lender),
248 auto const sleMPT2 = env.
le(mptoken);
249 BEAST_EXPECT(sleMPT2 !=
nullptr);
252 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
257 using namespace loan;
258 Number const principalRequest = broker.
asset(1'000).value();
261 "IOU authorized borrower, borrower submits, borrower has "
267 env(
pay(borrower, issuer, broker.
asset(10'000)));
270 auto const sleLine1 = env.
le(trustline);
271 BEAST_EXPECT(sleLine1 ==
nullptr);
274 env(
noop(borrower),
Fee(
XRP((acctReserve * 2) + (incReserve * 2))));
278 env(
set(borrower, broker.
brokerID, principalRequest),
279 kCounterparty(lender),
280 Sig(sfCounterpartySignature, lender),
282 Ter{tecNO_LINE_INSUF_RESERVE});
286 env(
pay(issuer, borrower,
XRP(incReserve)));
288 env(
set(borrower, broker.
brokerID, principalRequest),
289 kCounterparty(lender),
290 Sig(sfCounterpartySignature, lender),
294 auto const sleLine2 = env.
le(trustline);
295 BEAST_EXPECT(sleLine2 !=
nullptr);
297 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
301 using namespace loan;
302 Number const principalRequest = broker.
asset(1'000).value();
305 "MPT authorized borrower, borrower submits, lender has "
308 auto const sleMPT1 = env.
le(mptoken);
309 BEAST_EXPECT(sleMPT1 !=
nullptr);
311 env(
pay(lender, issuer, broker.
asset(sleMPT1->at(sfMPTAmount))));
314 mptt.
authorize({.account = lender, .flags = tfMPTUnauthorize});
317 auto const sleMPT2 = env.
le(mptoken);
318 BEAST_EXPECT(sleMPT2 ==
nullptr);
325 env(
set(borrower, broker.
brokerID, principalRequest),
326 kLoanOriginationFee(broker.
asset(1).value()),
327 kCounterparty(lender),
328 Sig(sfCounterpartySignature, lender),
330 Ter{tecINSUFFICIENT_RESERVE});
334 env(
pay(issuer, lender,
XRP(incReserve)));
336 env(
set(borrower, broker.
brokerID, principalRequest),
337 kLoanOriginationFee(broker.
asset(1).value()),
338 kCounterparty(lender),
339 Sig(sfCounterpartySignature, lender),
343 auto const sleMPT3 = env.
le(mptoken);
344 BEAST_EXPECT(sleMPT3 !=
nullptr);
347 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
352 using namespace loan;
353 Number const principalRequest = broker.
asset(1'000).value();
356 "IOU authorized borrower, borrower submits, lender has no "
363 auto const sleLine1 = env.
le(trustline);
364 BEAST_EXPECT(sleLine1 !=
nullptr);
366 env(
pay(lender, issuer, broker.
asset(
abs(sleLine1->at(sfBalance).value()))));
368 auto const sleLine2 = env.
le(trustline);
369 BEAST_EXPECT(sleLine2 ==
nullptr);
376 env(
set(borrower, broker.
brokerID, principalRequest),
377 kLoanOriginationFee(broker.
asset(1).value()),
378 kCounterparty(lender),
379 Sig(sfCounterpartySignature, lender),
381 Ter{tecNO_LINE_INSUF_RESERVE});
385 env(
pay(issuer, lender,
XRP(incReserve)));
387 env(
set(borrower, broker.
brokerID, principalRequest),
388 kLoanOriginationFee(broker.
asset(1).value()),
389 kCounterparty(lender),
390 Sig(sfCounterpartySignature, lender),
394 auto const sleLine3 = env.
le(trustline);
395 BEAST_EXPECT(sleLine3 !=
nullptr);
397 CaseArgs{.initialXRP = (acctReserve * 2) + (incReserve * 8) + 1});
401 using namespace loan;
402 Number const principalRequest = broker.
asset(1'000).value();
404 testcase(
"MPT authorized borrower, unauthorized lender");
406 auto const sleMPT1 = env.
le(mptoken);
407 BEAST_EXPECT(sleMPT1 !=
nullptr);
409 env(
pay(lender, issuer, broker.
asset(sleMPT1->at(sfMPTAmount))));
412 mptt.
authorize({.account = lender, .flags = tfMPTUnauthorize});
415 auto const sleMPT2 = env.
le(mptoken);
416 BEAST_EXPECT(sleMPT2 ==
nullptr);
419 env(
set(borrower, broker.
brokerID, principalRequest),
420 kLoanOriginationFee(broker.
asset(1).value()),
421 kCounterparty(lender),
422 Sig(sfCounterpartySignature, lender),
428 env(
set(borrower, broker.
brokerID, principalRequest),
429 kCounterparty(lender),
430 Sig(sfCounterpartySignature, lender),
436 auto const sleMPT3 = env.
le(mptoken);
437 BEAST_EXPECT(sleMPT3 ==
nullptr);
440 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
444 using namespace loan;
445 Number const principalRequest = broker.
asset(1'000).value();
447 testcase(
"MPT authorized borrower, borrower submits");
448 env(
set(borrower, broker.
brokerID, principalRequest),
449 kCounterparty(lender),
450 Sig(sfCounterpartySignature, lender),
454 using namespace loan;
455 Number const principalRequest = broker.
asset(1'000).value();
457 testcase(
"IOU authorized borrower, borrower submits");
458 env(
set(borrower, broker.
brokerID, principalRequest),
459 kCounterparty(lender),
460 Sig(sfCounterpartySignature, lender),
463 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
467 using namespace loan;
468 Number const principalRequest = broker.
asset(1'000).value();
470 testcase(
"MPT authorized borrower, lender submits");
471 env(
set(lender, broker.
brokerID, principalRequest),
472 kCounterparty(borrower),
473 Sig(sfCounterpartySignature, borrower),
477 using namespace loan;
478 Number const principalRequest = broker.
asset(1'000).value();
480 testcase(
"IOU authorized borrower, lender submits");
481 env(
set(lender, broker.
brokerID, principalRequest),
482 kCounterparty(borrower),
483 Sig(sfCounterpartySignature, borrower),
486 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
490 auto const msigSetup = [&](
Env& env,
Account const& account) {
498 using namespace loan;
499 msigSetup(env, lender);
500 Number const principalRequest = broker.
asset(1'000).value();
503 "MPT authorized borrower, borrower submits, lender "
505 env(
set(borrower, broker.
brokerID, principalRequest),
506 kCounterparty(lender),
507 Msig(sfCounterpartySignature, alice, bella),
511 using namespace loan;
512 msigSetup(env, lender);
513 Number const principalRequest = broker.
asset(1'000).value();
516 "IOU authorized borrower, borrower submits, lender "
518 env(
set(borrower, broker.
brokerID, principalRequest),
519 kCounterparty(lender),
520 Msig(sfCounterpartySignature, alice, bella),
523 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
527 using namespace loan;
528 msigSetup(env, borrower);
529 Number const principalRequest = broker.
asset(1'000).value();
532 "MPT authorized borrower, lender submits, borrower "
534 env(
set(lender, broker.
brokerID, principalRequest),
535 kCounterparty(borrower),
536 Msig(sfCounterpartySignature, alice, bella),
540 using namespace loan;
541 msigSetup(env, borrower);
542 Number const principalRequest = broker.
asset(1'000).value();
545 "IOU authorized borrower, lender submits, borrower "
547 env(
set(lender, broker.
brokerID, principalRequest),
548 kCounterparty(borrower),
549 Msig(sfCounterpartySignature, alice, bella),
552 CaseArgs{.requireAuth =
true, .authorizeBorrower =
true});
556 using namespace loan;
557 Number const principalRequest = broker.
asset(1'000).value();
558 Vault const vault{env};
559 auto tx = vault.set({.owner = lender, .
id = broker.
vaultID});
560 tx[sfAssetsMaximum] = BrokerParameters::defaults().vaultDeposit;
565 env(
set(issuer, broker.
brokerID, principalRequest),
566 kCounterparty(lender),
568 Sig(sfCounterpartySignature, lender),
576 using namespace loan;
577 Number const principalRequest = broker.
asset(1'000).value();
578 Vault const vault{env};
579 auto tx = vault.set({.owner = lender, .
id = broker.
vaultID});
580 tx[sfAssetsMaximum] =
581 BrokerParameters::defaults().vaultDeposit + broker.
asset(1).number();
585 testcase(
"Vault maximum value exceeded");
586 env(
set(issuer, broker.
brokerID, principalRequest),
587 kCounterparty(lender),
589 Sig(sfCounterpartySignature, lender),
592 kPaymentInterval(3600 * 24),
602 testcase(
"LoanSet closed-ended: phase and maturity bound");
604 using namespace loan;
606 Account const issuer{
"issuer"};
607 Account const lender{
"lender"};
608 Account const borrower{
"borrower"};
617 auto const withEnv = [&,
this](
auto&& body) {
619 env.
fund(
XRP(1'000'000'000), issuer, lender, borrower);
625 auto const setLoan = [&](
Env& env,
BrokerInfo const& broker,
TER expected) {
626 env(
set(lender, broker.brokerID, broker.asset(100).value()),
627 kCounterparty(borrower),
628 Sig(sfCounterpartySignature, borrower),
630 kPaymentTotal(kTotal),
631 kPaymentInterval(kInterval),
652 BEAST_EXPECT(broker.redemptionDate.has_value());
655 env.
close(tp{d{*broker.redemptionDate + 1}});
670 constexpr std::uint32_t kRedemptionOffset = 3u * 24u * 3600u;
677 env(
set(lender, broker.brokerID, broker.asset(100).value()),
678 kCounterparty(borrower),
679 Sig(sfCounterpartySignature, borrower),
682 kPaymentInterval(kInterval),
693 BEAST_EXPECT(broker.redemptionDate.has_value());
696 auto const acceptInterval = *broker.redemptionDate - 1 - startDate;
697 env(
set(lender, broker.brokerID, broker.asset(100).value()),
698 kCounterparty(borrower),
699 Sig(sfCounterpartySignature, borrower),
702 kPaymentInterval(acceptInterval),
706 auto const rejectInterval =
708 env(
set(lender, broker.brokerID, broker.asset(100).value()),
709 kCounterparty(borrower),
710 Sig(sfCounterpartySignature, borrower),
713 kPaymentInterval(rejectInterval),