90 auto const& asset = vault.asset.raw();
92 << (asset.native() ?
"XRP "
93 : asset.holds<
Issue>() ?
"IOU "
100 using namespace loanBroker;
107 return badMptt[
"BAD"];
109 static PrettyAsset const badIouAsset = evan[
"BAD"];
110 static Account const nonExistent{
"NonExistent"};
111 static PrettyAsset const ghostIouAsset = nonExistent[
"GST"];
112 PrettyAsset const vaultPseudoIouAsset = vault.pseudoAccount[
"PSD"];
117 auto const badBrokerPseudo = [&]() {
118 if (
auto const le = env.
le(badKeylet); BEAST_EXPECT(le))
120 return Account{
"Bad Broker pseudo-account", le->at(sfAccount)};
123 return vault.pseudoAccount;
125 PrettyAsset const badBrokerPseudoIouAsset = badBrokerPseudo[
"WAT"];
130 auto jtx = env.
jt(
set(alice, vault.vaultID));
133 jtx = modifyJTx(jtx);
139 if (
auto broker = env.
le(keylet); BEAST_EXPECT(broker))
143 BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
144 BEAST_EXPECT(broker->at(sfAccount) != alice.
id());
145 BEAST_EXPECT(broker->at(sfOwner) == alice.
id());
146 BEAST_EXPECT(broker->at(sfFlags) == 0);
147 BEAST_EXPECT(broker->at(sfSequence) == env.
seq(alice) - 1);
148 BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
149 BEAST_EXPECT(broker->at(sfLoanSequence) == 1);
150 BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
151 BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
161 Account const pseudoAccount{
"Broker pseudo-account", broker->at(sfAccount)};
164 if (
auto const pseudo = env.
le(pseudoKeylet); BEAST_EXPECT(pseudo))
170 BEAST_EXPECT(pseudo->at(sfSequence) == 0);
171 BEAST_EXPECT(pseudo->at(sfBalance) == beast::zero);
172 BEAST_EXPECT(pseudo->at(sfOwnerCount) == (vault.asset.raw().native() ? 0 : 1));
173 BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
174 BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
175 BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
176 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
177 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
178 BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
179 BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
180 BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
181 BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
182 BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
183 BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
184 BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
185 BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
186 BEAST_EXPECT(!pseudo->isFieldPresent(sfFirstNFTokenSequence));
187 BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
188 BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
189 BEAST_EXPECT(pseudo->at(sfLoanBrokerID) == keylet.
key);
195 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
196 if (BEAST_EXPECT(accountInfo.isObject()))
198 auto const& accountData = accountInfo[jss::result][jss::account_data];
199 if (BEAST_EXPECT(accountData.isObject()))
201 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
202 BEAST_EXPECT(accountData[sfLoanBrokerID] ==
to_string(keylet.
key));
204 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
205 if (BEAST_EXPECT(pseudoInfo.isObject()))
207 BEAST_EXPECT(pseudoInfo[jss::type] ==
"LoanBroker");
212 auto verifyCoverAmount = [&env, &vault, &pseudoAccount, &broker, &keylet,
this](
auto n) {
215 if (BEAST_EXPECT(broker = env.
le(keylet)))
217 auto const amount = vault.asset(n);
218 BEAST_EXPECT(broker->at(sfCoverAvailable) ==
amount.number());
228 env(coverDeposit(alice, vault.vaultID, vault.asset(10)),
ter(
tecNO_ENTRY));
230 verifyCoverAmount(0);
240 env(coverClawback(evan), loanBrokerID(vault.vaultID),
ter(
tecNO_ENTRY));
243 if (vault.asset.raw().native())
250 if (vault.asset.raw().holds<
Issue>())
260 env(coverClawback(issuer),
261 loanBrokerID(keylet.
key),
262 amount(badBrokerPseudoIouAsset(10)),
264 PrettyAsset const brokerWrongCurrencyAsset = pseudoAccount[
"WAT"];
265 env(coverClawback(issuer),
266 loanBrokerID(keylet.
key),
267 amount(brokerWrongCurrencyAsset(10)),
274 BEAST_EXPECT(vault.asset.raw().holds<
MPTIssue>());
279 env(coverClawback(issuer),
280 loanBrokerID(keylet.
key),
287 env(coverDeposit(alice, keylet.
key, vault.asset(10)));
289 verifyCoverAmount(10);
296 env(coverWithdraw(alice, vault.vaultID, vault.asset(10)),
ter(
tecNO_ENTRY));
300 if (!vault.asset.raw().native())
303 env(coverWithdraw(alice, keylet.
key, vault.asset(1)), destination(bystander),
ter(expected));
310 env(coverWithdraw(alice, keylet.
key, vault.asset(7)));
312 verifyCoverAmount(3);
315 env(coverDeposit(alice, keylet.
key, vault.asset(5)));
317 verifyCoverAmount(8);
321 env(coverWithdraw(alice, keylet.
key, vault.asset(1)), destination(evan));
323 verifyCoverAmount(7);
327 env(coverWithdraw(alice, keylet.
key, vault.asset(1)), destination(evan),
dtag(3));
329 verifyCoverAmount(6);
331 if (!vault.asset.raw().native())
334 env(coverClawback(issuer), loanBrokerID(keylet.
key),
amount(vault.asset(2)));
336 verifyCoverAmount(4);
339 env(coverDeposit(alice, keylet.
key, vault.asset(5)));
341 verifyCoverAmount(9);
344 for (
auto const& tx : {
348 coverClawback(issuer),
349 loanBrokerID(keylet.
key),
355 coverClawback(issuer),
356 loanBrokerID(keylet.
key),
363 coverClawback(issuer),
364 loanBrokerID(keylet.
key),
374 verifyCoverAmount(0);
377 env(coverDeposit(alice, keylet.
key, vault.asset(6)));
379 verifyCoverAmount(6);
384 env(
set(alice, vault.vaultID), loanBrokerID(keylet.
key));
389 changeBroker(broker);
394 if (BEAST_EXPECT(broker = env.
le(keylet)) && checkChangedBroker)
395 checkChangedBroker(broker);
400 env(
set(alice, vault.vaultID), loanBrokerID(broker->key()), debtMaximum(
Number(0)),
data(
""));
404 if (BEAST_EXPECT(broker = env.
le(keylet)))
406 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
407 BEAST_EXPECT(!broker->isFieldPresent(sfData));
417 env(del(alice, badKeylet.key));
422 auto const aliceBalance = env.
balance(alice, vault.asset);
423 auto const coverFunds = env.
balance(pseudoAccount, vault.asset);
424 BEAST_EXPECT(coverFunds.number() == broker->at(sfCoverAvailable));
425 BEAST_EXPECT(coverFunds != beast::zero);
426 verifyCoverAmount(6);
439 env(del(alice, keylet.
key));
442 broker = env.
le(keylet);
443 BEAST_EXPECT(!broker);
444 auto pseudo = env.
le(pseudoKeylet);
445 BEAST_EXPECT(!pseudo);
447 auto const expectedBalance = aliceBalance + coverFunds -
448 (aliceBalance.value().native() ?
STAmount(env.
current()->fees().base.value()) : vault.asset(0));
471 Account bystander{
"bystander"};
485 env(
trust(alice, iouAsset(1'000'000)));
486 env(
trust(evan, iouAsset(1'000'000)));
488 env(
pay(issuer, evan, iouAsset(100'000)));
489 env(
pay(issuer, alice, iouAsset(100'000)));
496 mptt.authorize({.account = alice});
497 mptt.authorize({.account = evan});
499 env(
pay(issuer, alice, mptAsset(100'000)));
500 env(
pay(issuer, evan, mptAsset(100'000)));
503 std::array const assets{xrpAsset, iouAsset, mptAsset};
507 for (
auto const& asset : assets)
509 auto [tx, keylet] = vault.create({.owner = alice, .asset = asset});
512 if (
auto const le = env.
le(keylet); BEAST_EXPECT(env.
le(keylet)))
517 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(50)}));
521 auto [tx, keylet] = vault.create({.owner = alice, .asset = iouAsset});
524 if (
auto const le = env.
le(keylet); BEAST_EXPECT(env.
le(keylet)))
526 return {iouAsset, keylet.
key, le->at(sfAccount)};
529 return {iouAsset, keylet.
key, evan.
id()};
532 auto const aliceOriginalCount = env.
ownerCount(alice);
535 for (
auto const& vault : vaults)
540 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
541 if (BEAST_EXPECT(accountInfo.isObject()))
543 auto const& accountData = accountInfo[jss::result][jss::account_data];
544 if (BEAST_EXPECT(accountData.isObject()))
546 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
547 BEAST_EXPECT(accountData[sfVaultID] ==
to_string(vault.vaultID));
549 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
550 if (BEAST_EXPECT(pseudoInfo.isObject()))
552 BEAST_EXPECT(pseudoInfo[jss::type] ==
"Vault");
557 using namespace loanBroker;
581 env(
set(evan, vault.vaultID),
582 coverRateMinimum(maxCoverRate),
583 coverRateLiquidation(maxCoverRate),
586 env(
set(evan, vault.vaultID),
587 coverRateMinimum(maxCoverRate + 1),
588 coverRateLiquidation(maxCoverRate + 1),
591 env(
set(evan, vault.vaultID),
592 coverRateMinimum(maxCoverRate / 2),
593 coverRateLiquidation(maxCoverRate + 1),
597 env(
set(evan, vault.vaultID), coverRateLiquidation(maxCoverRate),
ter(
temINVALID));
598 env(
set(evan, vault.vaultID),
599 coverRateMinimum(tenthBipsZero),
600 coverRateLiquidation(maxCoverRate),
604 env(
set(evan, vault.vaultID), coverRateMinimum(maxCoverRate),
ter(
temINVALID));
605 env(
set(evan, vault.vaultID),
606 coverRateMinimum(maxCoverRate),
607 coverRateLiquidation(tenthBipsZero),
630 BEAST_EXPECT(!broker->isFieldPresent(sfManagementFeeRate));
631 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateMinimum));
632 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateLiquidation));
633 BEAST_EXPECT(!broker->isFieldPresent(sfData));
634 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
635 BEAST_EXPECT(broker->at(sfDebtMaximum) == 0);
636 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 0);
637 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 0);
639 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount + 4);
649 env(
set(alice, vault.vaultID), loanBrokerID(nextKeylet.key),
ter(
tecNO_ENTRY), THISLINE);
651 env(
set(alice, nextKeylet.key), loanBrokerID(broker->key()),
ter(
tecNO_ENTRY), THISLINE);
655 env(
set(alice, vault.vaultID),
656 loanBrokerID(broker->key()),
657 managementFeeRate(maxManagementFeeRate),
661 env(
set(alice, vault.vaultID),
662 loanBrokerID(broker->key()),
663 coverRateMinimum(maxManagementFeeRate),
667 env(
set(alice, vault.vaultID),
668 loanBrokerID(broker->key()),
669 coverRateLiquidation(maxManagementFeeRate),
674 testData =
"Test Data 1234";
676 env(
set(alice, vault.vaultID),
677 loanBrokerID(broker->key()),
683 env(
set(alice, vault.vaultID),
684 loanBrokerID(broker->key()),
685 debtMaximum(
Number(-175, -1)),
689 if (vault.asset.integral())
691 env(
set(alice, vault.vaultID),
692 loanBrokerID(broker->key()),
694 debtMaximum(debtMax),
700 env(
set(alice, vault.vaultID),
701 loanBrokerID(broker->key()),
703 debtMaximum(debtMax),
708 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
710 auto const actual = broker->at(sfDebtMaximum);
712 actual == expected,
"Expected: " +
to_string(expected) +
", Actual: " +
to_string(actual));
716 "non-default fields",
725 testData =
"spam spam spam spam";
738 BEAST_EXPECT(broker->at(sfManagementFeeRate) == 123);
739 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 100);
740 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 200);
741 BEAST_EXPECT(broker->at(sfDebtMaximum) ==
Number(9));
742 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
746 env(
set(alice, vault.vaultID), loanBrokerID(broker->key()),
data(
""), debtMaximum(
Number(0)));
750 BEAST_EXPECT(!broker->isFieldPresent(sfData));
751 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
755 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount);
766 using namespace loanBroker;
767 Account const issuer{
"issuer"};
772 env.
fund(
XRP(100'000), issuer, alice);
777 return getAsset(env, issuer, alice);
778 env(
trust(alice, issuer[
"IOU"](1'000'000)), THISLINE);
783 env(
pay(issuer, alice, asset(100'000)), THISLINE);
786 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
789 auto const le = env.
le(vaultKeylet);
791 if (BEAST_EXPECT(le))
792 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
798 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}), THISLINE);
802 env(
set(alice, vaultInfo.vaultID), THISLINE);
805 auto broker = env.
le(brokerKeylet);
806 if (!BEAST_EXPECT(broker))
809 auto testZeroBrokerID = [&](
auto&& getTxJv) {
812 jv[sfLoanBrokerID] =
"";
820 auto testZeroVaultID = [&](
auto&& getTxJv) {
835 testZeroBrokerID([&]() {
return coverDeposit(alice, brokerKeylet.key, asset(10)); });
841 env(
pay(alice, issuer, asset(100'000 - 50)), THISLINE);
852 env(
coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), THISLINE);
858 testZeroBrokerID([&]() {
return coverWithdraw(alice, brokerKeylet.key, asset(10)); });
864 Account
const bogus{
"bogus"};
868 Account
const dest{
"dest"};
881 env(
trust(dest, asset(1'000)), THISLINE);
902 testZeroBrokerID([&]() {
906 if (asset.holds<
Issue>())
911 amount(vaultInfo.asset(2)),
920 amount(vaultInfo.asset(2)),
929 amount(vaultInfo.asset(2)),
938 Account
const borrower{
"borrower"};
939 env.
fund(
XRP(1'000), borrower);
940 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
941 sig(sfCounterpartySignature, alice),
942 fee(env.
current()->fees().base * 2),
946 testZeroBrokerID([&]() {
return del(alice, brokerKeylet.key); });
952 auto const loanKeylet =
keylet::loan(brokerKeylet.key, 1);
953 env(
loan::pay(borrower, loanKeylet.key, asset(50).value()), THISLINE);
954 env(
loan::del(alice, loanKeylet.key), THISLINE);
958 env(
del(alice, brokerKeylet.key), ter(
tecFROZEN), THISLINE);
962 env(
del(alice, brokerKeylet.key), ter(
tesSUCCESS), THISLINE);
965 env(
del(alice, brokerKeylet.key), THISLINE);
967 if (brokerTest ==
Set)
970 testZeroBrokerID([&]() {
return env.
json(
set(alice, vaultInfo.vaultID),
loanBrokerID(brokerKeylet.key)); });
972 testZeroVaultID([&]() {
return env.
json(
set(alice, vaultInfo.vaultID),
loanBrokerID(brokerKeylet.key)); });
974 if (asset.holds<
Issue>())
986 env(
pay(alice, issuer, amt), THISLINE);
1159 testcase(
"Require Auth - Implicit Pseudo-account authorization");
1160 using namespace jtx;
1161 using namespace loanBroker;
1163 Account const issuer{
"issuer"};
1168 env.
fund(
XRP(100'000), issuer, alice);
1179 env(
pay(issuer, alice, asset(100'000)));
1183 asset.authorize({.account = issuer, .holder = alice, .flags =
tfMPTUnauthorize});
1184 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1188 auto const le = env.
le(vaultKeylet);
1190 if (BEAST_EXPECT(le))
1191 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
1201 auto forUnauthAuth = [&](
auto&& doTx) {
1204 asset.authorize({.account = issuer, .holder = alice, .flags = flag});
1214 env(
vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(51)}), err);
1220 env(
vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = asset(1)}), err);
1225 forUnauthAuth([&](
auto) { env(
set(alice, vaultInfo.vaultID)); });
1227 auto const broker = env.
le(brokerKeylet);
1228 if (!BEAST_EXPECT(broker))
1230 Account brokerPseudo(
"pseudo", broker->at(sfAccount));
1239 env(
coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)), err);
1245 env(
coverWithdraw(alice, brokerKeylet.key, vaultInfo.asset(5)), err);
1462 using namespace jtx;
1466 auto const token = issuer[
"IOU"];
1476 auto test = [&](TrustState trustState) {
1479 testcase <<
"RIPD-4274 IOU with state: " <<
static_cast<int>(trustState);
1481 auto setTrustLine = [&](
Account const& acct, TrustState state) {
1485 env(trust(issuer, token(0), acct,
tfSetfAuth));
1488 auto jv = trust(acct, token(0));
1491 jv[sfQualityIn] = 10'000'000;
1495 case ReachedLimit: {
1496 env(trust(acct, token(1'000)));
1497 env(pay(issuer, acct, token(1'000)));
1502 env(trust(acct, token(1'000)));
1503 env(pay(issuer, acct, token(950)));
1511 BEAST_EXPECT(
false);
1516 env.
fund(
XRP(1'000), issuer, broker, dest);
1519 if (trustState == RequireAuth)
1524 setTrustLine(broker, RequireAuth);
1527 setTrustLine(dest, trustState);
1529 env(trust(broker, token(2'000), 0));
1530 env(pay(issuer, broker, token(2'000)));
1534 auto const [tx, keylet] = vault.create({.owner = broker, .asset = token.asset()});
1539 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
1542 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
1543 loanBroker::destination(dest),
1548 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
1551 auto const brokerKeylet = keylet::loanbroker(broker, env.
seq(broker));
1553 env(loanBroker::set(broker, keylet.
key));
1556 env(loanBroker::coverDeposit(broker, brokerKeylet.key, token(1'000)));
1559 env(loanBroker::coverWithdraw(broker, brokerKeylet.key, token(100)),
1560 loanBroker::destination(dest),
1566 if (trustState == RequireAuth)
1571 env(loanBroker::coverWithdraw(broker, brokerKeylet.key, token(100)),
1572 loanBroker::destination(dest),
1589 using namespace jtx;
1600 auto test = [&](MPTState MPTState) {
1603 testcase <<
"RIPD-4274 MPT with state: " <<
static_cast<int>(MPTState);
1605 env.
fund(
XRP(1'000), issuer, broker, dest);
1615 .holders = {broker, dest},
1628 .holders = {broker, dest},
1632 BEAST_EXPECT(env.
balance(issuer, tester) == tester(-4'000));
1639 .holders = {broker},
1648 if (!BEAST_EXPECT(maybeToken))
1651 auto const& token = *maybeToken;
1654 auto const [tx, keylet] = vault.create({.owner = broker, .asset = token.asset()});
1659 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
1662 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
1663 loanBroker::destination(dest),
1668 BEAST_EXPECT(env.
ter() == err);
1673 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
1677 auto const brokerKeylet = keylet::loanbroker(broker, env.
seq(broker));
1679 env(loanBroker::set(broker, keylet.
key));
1682 env(loanBroker::coverDeposit(broker, brokerKeylet.key, token(1'000)));
1685 env(loanBroker::coverWithdraw(broker, brokerKeylet.key, token(100)),
1686 loanBroker::destination(dest),
1688 BEAST_EXPECT(env.
ter() == err);