103 auto const& asset = vault.asset.raw();
105 << (asset.native() ?
"XRP "
106 : asset.holds<
Issue>() ?
"IOU "
113 using namespace loanBroker;
121 return badMptt[
"BAD"];
123 static PrettyAsset const badIouAsset = evan[
"BAD"];
124 static Account const nonExistent{
"NonExistent"};
125 static PrettyAsset const ghostIouAsset = nonExistent[
"GST"];
126 PrettyAsset const vaultPseudoIouAsset = vault.pseudoAccount[
"PSD"];
131 auto const badBrokerPseudo = [&]() {
132 if (
auto const le = env.
le(badKeylet); BEAST_EXPECT(le))
134 return Account{
"Bad Broker pseudo-account", le->at(sfAccount)};
137 return vault.pseudoAccount;
139 PrettyAsset const badBrokerPseudoIouAsset = badBrokerPseudo[
"WAT"];
144 auto jtx = env.
jt(
set(alice, vault.vaultID));
147 jtx = modifyJTx(jtx);
153 if (
auto broker = env.
le(keylet); BEAST_EXPECT(broker))
157 BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
158 BEAST_EXPECT(broker->at(sfAccount) != alice.
id());
159 BEAST_EXPECT(broker->at(sfOwner) == alice.
id());
160 BEAST_EXPECT(broker->at(sfFlags) == 0);
161 BEAST_EXPECT(broker->at(sfSequence) == env.
seq(alice) - 1);
162 BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
163 BEAST_EXPECT(broker->at(sfLoanSequence) == 1);
164 BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
165 BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
176 "Broker pseudo-account", broker->at(sfAccount)};
179 if (
auto const pseudo = env.
le(pseudoKeylet); BEAST_EXPECT(pseudo))
185 pseudo->at(sfFlags) ==
187 BEAST_EXPECT(pseudo->at(sfSequence) == 0);
188 BEAST_EXPECT(pseudo->at(sfBalance) == beast::zero);
190 pseudo->at(sfOwnerCount) ==
191 (vault.asset.raw().native() ? 0 : 1));
192 BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
193 BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
194 BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
195 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
196 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
197 BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
198 BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
199 BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
200 BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
201 BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
202 BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
203 BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
204 BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
205 BEAST_EXPECT(!pseudo->isFieldPresent(sfFirstNFTokenSequence));
206 BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
207 BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
208 BEAST_EXPECT(pseudo->at(sfLoanBrokerID) == keylet.
key);
214 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
215 if (BEAST_EXPECT(accountInfo.isObject()))
217 auto const& accountData =
218 accountInfo[jss::result][jss::account_data];
219 if (BEAST_EXPECT(accountData.isObject()))
221 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
223 accountData[sfLoanBrokerID] ==
226 auto const& pseudoInfo =
227 accountInfo[jss::result][jss::pseudo_account];
228 if (BEAST_EXPECT(pseudoInfo.isObject()))
230 BEAST_EXPECT(pseudoInfo[jss::type] ==
"LoanBroker");
235 auto verifyCoverAmount =
236 [&env, &vault, &pseudoAccount, &broker, &keylet,
this](
auto n) {
239 if (BEAST_EXPECT(broker = env.
le(keylet)))
241 auto const amount = vault.asset(n);
243 broker->at(sfCoverAvailable) ==
amount.number());
249 env(coverDeposit(alice,
uint256(0), vault.asset(10)),
251 env(coverDeposit(evan, keylet.
key, vault.asset(10)),
253 env(coverDeposit(evan, keylet.
key, vault.asset(0)),
255 env(coverDeposit(evan, keylet.
key, vault.asset(-10)),
257 env(coverDeposit(alice, vault.vaultID, vault.asset(10)),
260 verifyCoverAmount(0);
265 env(coverClawback(alice),
269 env(coverClawback(alice),
274 env(coverClawback(evan),
275 loanBrokerID(vault.vaultID),
278 env(coverClawback(alice),
279 loanBrokerID(keylet.
key),
281 if (vault.asset.raw().native())
284 env(coverClawback(issuer),
285 loanBrokerID(keylet.
key),
290 if (vault.asset.raw().holds<
Issue>())
294 env(coverClawback(alice),
297 env(coverClawback(alice),
301 env(coverClawback(alice),
302 amount(vaultPseudoIouAsset(1)),
306 env(coverClawback(issuer),
307 loanBrokerID(keylet.
key),
308 amount(badBrokerPseudoIouAsset(10)),
311 pseudoAccount[
"WAT"];
312 env(coverClawback(issuer),
313 loanBrokerID(keylet.
key),
314 amount(brokerWrongCurrencyAsset(10)),
321 BEAST_EXPECT(vault.asset.raw().holds<
MPTIssue>());
322 env(coverClawback(alice),
328 env(coverClawback(issuer),
329 loanBrokerID(keylet.
key),
336 env(coverDeposit(alice, keylet.
key, vault.asset(10)));
338 verifyCoverAmount(10);
341 env(coverWithdraw(alice,
uint256(0), vault.asset(10)),
343 env(coverWithdraw(evan, keylet.
key, vault.asset(10)),
345 env(coverWithdraw(evan, keylet.
key, vault.asset(0)),
347 env(coverWithdraw(evan, keylet.
key, vault.asset(-10)),
349 env(coverWithdraw(alice, vault.vaultID, vault.asset(10)),
351 env(coverWithdraw(alice, keylet.
key, vault.asset(900)),
355 if (!vault.asset.raw().native())
357 TER const expected = vault.asset.raw().holds<
MPTIssue>()
360 env(coverWithdraw(alice, keylet.
key, vault.asset(1)),
361 destination(bystander),
366 env(coverWithdraw(alice, keylet.
key, vault.asset(1)),
371 env(coverWithdraw(alice, keylet.
key, vault.asset(7)));
373 verifyCoverAmount(3);
376 env(coverDeposit(alice, keylet.
key, vault.asset(5)));
378 verifyCoverAmount(8);
382 env(coverWithdraw(alice, keylet.
key, vault.asset(1)),
385 verifyCoverAmount(7);
389 env(coverWithdraw(alice, keylet.
key, vault.asset(1)),
393 verifyCoverAmount(6);
395 if (!vault.asset.raw().native())
398 env(coverClawback(issuer),
399 loanBrokerID(keylet.
key),
402 verifyCoverAmount(4);
405 env(coverDeposit(alice, keylet.
key, vault.asset(5)));
407 verifyCoverAmount(9);
410 for (
auto const& tx : {
413 coverClawback(issuer),
414 loanBrokerID(keylet.
key),
419 coverClawback(issuer),
420 loanBrokerID(keylet.
key),
426 coverClawback(issuer),
427 loanBrokerID(keylet.
key),
434 coverClawback(issuer),
435 loanBrokerID(keylet.
key),
445 verifyCoverAmount(0);
448 env(coverDeposit(alice, keylet.
key, vault.asset(6)));
450 verifyCoverAmount(6);
455 env(
set(alice, vault.vaultID), loanBrokerID(keylet.
key));
460 changeBroker(broker);
465 if (BEAST_EXPECT(broker = env.
le(keylet)) && checkChangedBroker)
466 checkChangedBroker(broker);
471 env(
set(alice, vault.vaultID),
472 loanBrokerID(broker->key()),
478 if (BEAST_EXPECT(broker = env.
le(keylet)))
480 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
481 BEAST_EXPECT(!broker->isFieldPresent(sfData));
491 env(del(alice, badKeylet.key));
496 auto const aliceBalance = env.
balance(alice, vault.asset);
497 auto const coverFunds = env.
balance(pseudoAccount, vault.asset);
498 BEAST_EXPECT(coverFunds.number() == broker->at(sfCoverAvailable));
499 BEAST_EXPECT(coverFunds != beast::zero);
500 verifyCoverAmount(6);
513 env(del(alice, keylet.
key));
516 broker = env.
le(keylet);
517 BEAST_EXPECT(!broker);
518 auto pseudo = env.
le(pseudoKeylet);
519 BEAST_EXPECT(!pseudo);
521 auto const expectedBalance = aliceBalance + coverFunds -
522 (aliceBalance.value().native()
547 Account bystander{
"bystander"};
561 env(
trust(alice, iouAsset(1'000'000)));
562 env(
trust(evan, iouAsset(1'000'000)));
564 env(
pay(issuer, evan, iouAsset(100'000)));
565 env(
pay(issuer, alice, iouAsset(100'000)));
573 mptt.authorize({.account = alice});
574 mptt.authorize({.account = evan});
576 env(
pay(issuer, alice, mptAsset(100'000)));
577 env(
pay(issuer, evan, mptAsset(100'000)));
580 std::array const assets{xrpAsset, iouAsset, mptAsset};
584 for (
auto const& asset : assets)
586 auto [tx, keylet] = vault.create({.owner = alice, .asset = asset});
589 if (
auto const le = env.
le(keylet); BEAST_EXPECT(env.
le(keylet)))
595 {.depositor = alice, .id = keylet.key, .amount = asset(50)}));
600 vault.create({.owner = alice, .asset = iouAsset});
603 if (
auto const le = env.
le(keylet); BEAST_EXPECT(env.
le(keylet)))
605 return {iouAsset, keylet.
key, le->at(sfAccount)};
608 return {iouAsset, keylet.
key, evan.
id()};
611 auto const aliceOriginalCount = env.
ownerCount(alice);
614 for (
auto const& vault : vaults)
620 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
621 if (BEAST_EXPECT(accountInfo.isObject()))
623 auto const& accountData =
624 accountInfo[jss::result][jss::account_data];
625 if (BEAST_EXPECT(accountData.isObject()))
627 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
629 accountData[sfVaultID] ==
to_string(vault.vaultID));
631 auto const& pseudoInfo =
632 accountInfo[jss::result][jss::pseudo_account];
633 if (BEAST_EXPECT(pseudoInfo.isObject()))
635 BEAST_EXPECT(pseudoInfo[jss::type] ==
"Vault");
640 using namespace loanBroker;
655 env(
set(evan, vault.vaultID),
659 env(
set(evan, vault.vaultID),
663 env(
set(evan, vault.vaultID),
664 managementFeeRate(maxManagementFeeRate),
667 env(
set(evan, vault.vaultID),
668 managementFeeRate(maxManagementFeeRate +
TenthBips16(10)),
672 env(
set(evan, vault.vaultID),
673 coverRateMinimum(maxCoverRate),
674 coverRateLiquidation(maxCoverRate),
677 env(
set(evan, vault.vaultID),
678 coverRateMinimum(maxCoverRate + 1),
679 coverRateLiquidation(maxCoverRate + 1),
682 env(
set(evan, vault.vaultID),
683 coverRateMinimum(maxCoverRate / 2),
684 coverRateLiquidation(maxCoverRate + 1),
688 env(
set(evan, vault.vaultID),
689 coverRateLiquidation(maxCoverRate),
691 env(
set(evan, vault.vaultID),
692 coverRateMinimum(tenthBipsZero),
693 coverRateLiquidation(maxCoverRate),
697 env(
set(evan, vault.vaultID),
698 coverRateMinimum(maxCoverRate),
700 env(
set(evan, vault.vaultID),
701 coverRateMinimum(maxCoverRate),
702 coverRateLiquidation(tenthBipsZero),
705 env(
set(evan, vault.vaultID),
709 env(
set(evan, vault.vaultID),
710 debtMaximum(
Number(1, 100)),
713 env(
set(evan, vault.vaultID),
731 BEAST_EXPECT(!broker->isFieldPresent(sfManagementFeeRate));
732 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateMinimum));
734 !broker->isFieldPresent(sfCoverRateLiquidation));
735 BEAST_EXPECT(!broker->isFieldPresent(sfData));
736 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
737 BEAST_EXPECT(broker->at(sfDebtMaximum) == 0);
738 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 0);
739 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 0);
742 env.
ownerCount(alice) == aliceOriginalCount + 4);
748 auto const nextKeylet =
753 env(
set(alice, vault.vaultID),
754 loanBrokerID(nextKeylet.key),
757 env(
set(alice, nextKeylet.key),
758 loanBrokerID(broker->key()),
761 env(
set(evan, vault.vaultID),
762 loanBrokerID(broker->key()),
765 env(
set(alice, vault.vaultID),
766 loanBrokerID(broker->key()),
767 managementFeeRate(maxManagementFeeRate),
770 env(
set(alice, vault.vaultID),
771 loanBrokerID(broker->key()),
772 coverRateMinimum(maxManagementFeeRate),
775 env(
set(alice, vault.vaultID),
776 loanBrokerID(broker->key()),
777 coverRateLiquidation(maxManagementFeeRate),
781 testData =
"Test Data 1234";
783 env(
set(alice, vault.vaultID),
784 loanBrokerID(broker->key()),
789 env(
set(alice, vault.vaultID),
790 loanBrokerID(broker->key()),
791 debtMaximum(
Number(-175, -1)),
794 env(
set(alice, vault.vaultID),
795 loanBrokerID(broker->key()),
797 debtMaximum(
Number(175, -1)));
801 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
802 BEAST_EXPECT(broker->at(sfDebtMaximum) ==
Number(175, -1));
806 "non-default fields",
815 testData =
"spam spam spam spam";
828 BEAST_EXPECT(broker->at(sfManagementFeeRate) == 123);
829 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 100);
830 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 200);
831 BEAST_EXPECT(broker->at(sfDebtMaximum) ==
Number(9));
832 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
836 env(
set(alice, vault.vaultID),
837 loanBrokerID(broker->key()),
843 BEAST_EXPECT(!broker->isFieldPresent(sfData));
844 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
848 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount);
868 using namespace loanBroker;
869 Account const issuer{
"issuer"};
874 env.
fund(
XRP(100'000), issuer, alice);
879 return getAsset(env, issuer, alice);
880 env(
trust(alice, issuer[
"IOU"](1'000'000)), THISLINE);
885 env(
pay(issuer, alice, asset(100'000)), THISLINE);
888 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
891 auto const le = env.
le(vaultKeylet);
893 if (BEAST_EXPECT(le))
894 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
902 .id = vaultKeylet.key,
903 .amount = asset(50)}),
907 auto const brokerKeylet =
909 env(
set(alice, vaultInfo.vaultID), THISLINE);
912 auto broker = env.
le(brokerKeylet);
913 if (!BEAST_EXPECT(broker))
916 auto testZeroBrokerID = [&](
auto&& getTxJv) {
919 jv[sfLoanBrokerID] =
"";
927 auto testZeroVaultID = [&](
auto&& getTxJv) {
942 testZeroBrokerID([&]() {
943 return coverDeposit(alice, brokerKeylet.key, asset(10));
947 env(
coverDeposit(alice, brokerKeylet.key, issuer[
"BAD"](10)),
952 env(
pay(alice, issuer, asset(100'000 - 50)), THISLINE);
954 env(
coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)),
960 env(
coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)),
966 env(
coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)),
973 testZeroBrokerID([&]() {
978 env(
coverWithdraw(alice, brokerKeylet.key, issuer[
"BAD"](10)),
983 Account
const bogus{
"bogus"};
990 Account
const dest{
"dest"};
1009 env(
trust(dest, asset(1'000)), THISLINE);
1032 testZeroBrokerID([&]() {
1036 amount(vaultInfo.asset(2)));
1039 if (asset.holds<
Issue>())
1044 amount(vaultInfo.asset(2)),
1054 amount(vaultInfo.asset(2)),
1063 amount(vaultInfo.asset(2)),
1070 if (brokerTest ==
Delete)
1072 Account
const borrower{
"borrower"};
1073 env.
fund(
XRP(1'000), borrower);
1074 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
1075 sig(sfCounterpartySignature, alice),
1076 fee(env.
current()->fees().base * 2),
1080 testZeroBrokerID([&]() {
return del(alice, brokerKeylet.key); });
1083 env(
del(alice, brokerKeylet.key),
1088 auto const loanKeylet =
keylet::loan(brokerKeylet.key, 1);
1089 env(
loan::pay(borrower, loanKeylet.key, asset(50).value()),
1091 env(
loan::del(alice, loanKeylet.key), THISLINE);
1096 env(
del(alice, brokerKeylet.key), ter(
tecFROZEN), THISLINE);
1102 env(
del(alice, brokerKeylet.key), ter(
tesSUCCESS), THISLINE);
1105 env(
del(alice, brokerKeylet.key), THISLINE);
1107 if (brokerTest ==
Set)
1110 testZeroBrokerID([&]() {
1112 set(alice, vaultInfo.vaultID),
1116 testZeroVaultID([&]() {
1118 set(alice, vaultInfo.vaultID),
1122 if (asset.holds<
Issue>())
1133 auto const amt = env.
balance(alice) -
1135 env(
pay(alice, issuer, amt), THISLINE);
1138 env(
set(alice, vaultInfo.vaultID),