2#include <test/jtx/Account.h>
3#include <test/jtx/CaptureLogs.h>
4#include <test/jtx/Env.h>
5#include <test/jtx/JTx.h>
6#include <test/jtx/TestHelpers.h>
7#include <test/jtx/amount.h>
8#include <test/jtx/balance.h>
9#include <test/jtx/fee.h>
10#include <test/jtx/flags.h>
11#include <test/jtx/mpt.h>
12#include <test/jtx/pay.h>
13#include <test/jtx/seq.h>
14#include <test/jtx/sig.h>
15#include <test/jtx/tag.h>
16#include <test/jtx/tags.h>
17#include <test/jtx/ter.h>
18#include <test/jtx/trust.h>
19#include <test/jtx/txflags.h>
20#include <test/jtx/vault.h>
21#include <test/unit_test/SuiteJournal.h>
23#include <xrpl/basics/Number.h>
24#include <xrpl/basics/base_uint.h>
25#include <xrpl/basics/strHex.h>
26#include <xrpl/beast/unit_test/suite.h>
27#include <xrpl/beast/utility/Journal.h>
28#include <xrpl/beast/utility/Zero.h>
29#include <xrpl/core/ServiceRegistry.h>
30#include <xrpl/ledger/ApplyView.h>
31#include <xrpl/ledger/OpenView.h>
32#include <xrpl/ledger/helpers/AccountRootHelpers.h>
33#include <xrpl/protocol/AccountID.h>
34#include <xrpl/protocol/Feature.h>
35#include <xrpl/protocol/Indexes.h>
36#include <xrpl/protocol/Issue.h>
37#include <xrpl/protocol/LedgerFormats.h>
38#include <xrpl/protocol/MPTIssue.h>
39#include <xrpl/protocol/Protocol.h>
40#include <xrpl/protocol/SField.h>
41#include <xrpl/protocol/STAmount.h>
42#include <xrpl/protocol/STObject.h>
43#include <xrpl/protocol/STTx.h>
44#include <xrpl/protocol/SeqProxy.h>
45#include <xrpl/protocol/Serializer.h>
46#include <xrpl/protocol/TER.h>
47#include <xrpl/protocol/TxFlags.h>
48#include <xrpl/protocol/TxFormats.h>
49#include <xrpl/protocol/Units.h>
50#include <xrpl/protocol/jss.h>
51#include <xrpl/tx/ApplyContext.h>
52#include <xrpl/tx/Transactor.h>
53#include <xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h>
83 auto failAll = [
this](
FeatureBitset features,
bool goodVault =
false) {
84 Env env(*
this, features);
91 Vault const vault{env};
92 auto const [tx,
keylet] = vault.create({.owner = alice, .asset = asset});
95 BEAST_EXPECT(
static_cast<bool>(env.
le(
keylet)) == goodVault);
100 auto const brokerKeylet =
104 env(coverDeposit(alice, brokerKeylet.key, asset(1000)),
Ter(
temDISABLED));
106 env(coverWithdraw(alice, brokerKeylet.key, asset(1000)),
Ter(
temDISABLED));
109 env(coverClawback(alice), kLoanBrokerId(brokerKeylet.key),
Ter(
temDISABLED));
111 env(coverClawback(alice),
112 kLoanBrokerId(brokerKeylet.key),
118 failAll(
all_ - featureMPTokensV1);
119 failAll(
all_ - featureSingleAssetVault - featureLendingProtocol);
120 failAll(
all_ - featureSingleAssetVault);
121 failAll(
all_ - featureLendingProtocol,
true);
151 auto const& asset = vault.asset.raw();
157 else if (asset.holds<
Issue>())
167 assetLabel =
"Unknown ";
169 testcase <<
"Lifecycle: " << assetLabel << label;
178 badMptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
180 return badMptt[
"BAD"];
182 static PrettyAsset const kBadIouAsset = evan[
"BAD"];
183 static Account const kNonExistent{
"NonExistent"};
184 static PrettyAsset const kGhostIouAsset = kNonExistent[
"GST"];
185 PrettyAsset const vaultPseudoIouAsset = vault.pseudoAccount[
"PSD"];
187 auto const badKeylet =
191 auto const badBrokerPseudo = [&]() {
192 if (
auto const le = env.
le(badKeylet); BEAST_EXPECT(le))
194 return Account{
"Bad Broker pseudo-account", le->at(sfAccount)};
197 return vault.pseudoAccount;
199 PrettyAsset const badBrokerPseudoIouAsset = badBrokerPseudo[
"WAT"];
204 auto jtx = env.
jt(
set(alice, vault.vaultID));
213 if (
auto broker = env.
le(
keylet); BEAST_EXPECT(broker))
217 BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
218 BEAST_EXPECT(broker->at(sfAccount) != alice.
id());
219 BEAST_EXPECT(broker->at(sfOwner) == alice.
id());
220 BEAST_EXPECT(broker->at(sfFlags) == 0);
221 BEAST_EXPECT(broker->at(sfSequence) == env.
seq(alice) - 1);
222 BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
223 BEAST_EXPECT(broker->at(sfLoanSequence) == 1);
224 BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
225 BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
235 Account const pseudoAccount{
"Broker pseudo-account", broker->at(sfAccount)};
238 if (
auto const pseudo = env.
le(pseudoKeylet); BEAST_EXPECT(pseudo))
244 pseudo->at(sfFlags) == (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth));
245 BEAST_EXPECT(pseudo->at(sfSequence) == 0);
247 BEAST_EXPECT(pseudo->at(sfOwnerCount) == (vault.asset.raw().native() ? 0 : 1));
248 BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
249 BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
250 BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
251 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
252 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
253 BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
254 BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
255 BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
256 BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
257 BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
258 BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
259 BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
260 BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
261 BEAST_EXPECT(!pseudo->isFieldPresent(sfFirstNFTokenSequence));
262 BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
263 BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
264 BEAST_EXPECT(pseudo->at(sfLoanBrokerID) ==
keylet.key);
270 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
271 if (BEAST_EXPECT(accountInfo.isObject()))
273 auto const& accountData = accountInfo[jss::result][jss::account_data];
274 if (BEAST_EXPECT(accountData.isObject()))
276 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
279 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
280 if (BEAST_EXPECT(pseudoInfo.isObject()))
282 BEAST_EXPECT(pseudoInfo[jss::type] ==
"LoanBroker");
287 auto verifyCoverAmount =
288 [&env, &vault, &pseudoAccount, &broker, &
keylet,
this](
auto n) {
292 if (BEAST_EXPECT(broker))
294 auto const amount = vault.asset(n);
295 BEAST_EXPECT(broker->at(sfCoverAvailable) == amount.number());
305 env(coverDeposit(alice, vault.vaultID, vault.asset(10)),
Ter(
tecNO_ENTRY));
307 verifyCoverAmount(0);
317 env(coverClawback(evan), kLoanBrokerId(vault.vaultID),
Ter(
tecNO_ENTRY));
320 if (vault.asset.raw().native())
327 if (vault.asset.raw().holds<
Issue>())
334 env(coverClawback(alice),
335 kAmount(vaultPseudoIouAsset(1)),
339 env(coverClawback(issuer),
340 kLoanBrokerId(
keylet.key),
341 kAmount(badBrokerPseudoIouAsset(10)),
343 PrettyAsset const brokerWrongCurrencyAsset = pseudoAccount[
"WAT"];
344 env(coverClawback(issuer),
345 kLoanBrokerId(
keylet.key),
346 kAmount(brokerWrongCurrencyAsset(10)),
353 BEAST_EXPECT(vault.asset.raw().holds<
MPTIssue>());
358 env(coverClawback(issuer),
359 kLoanBrokerId(
keylet.key),
366 env(coverDeposit(alice,
keylet.key, vault.asset(10)));
368 verifyCoverAmount(10);
375 env(coverWithdraw(alice, vault.vaultID, vault.asset(10)),
Ter(
tecNO_ENTRY));
379 if (!vault.asset.raw().native())
382 env(coverWithdraw(alice,
keylet.key, vault.asset(1)),
383 kDestination(bystander),
388 env(coverWithdraw(alice,
keylet.key, vault.asset(1)),
393 env(coverWithdraw(alice,
keylet.key, vault.asset(7)));
395 verifyCoverAmount(3);
398 env(coverDeposit(alice,
keylet.key, vault.asset(5)));
400 verifyCoverAmount(8);
404 env(coverWithdraw(alice,
keylet.key, vault.asset(1)), kDestination(evan));
406 verifyCoverAmount(7);
410 env(coverWithdraw(alice,
keylet.key, vault.asset(1)), kDestination(evan),
Dtag(3));
412 verifyCoverAmount(6);
414 if (!vault.asset.raw().native())
417 env(coverClawback(issuer), kLoanBrokerId(
keylet.key),
kAmount(vault.asset(2)));
419 verifyCoverAmount(4);
422 env(coverDeposit(alice,
keylet.key, vault.asset(5)));
424 verifyCoverAmount(9);
427 for (
auto const& tx : {
430 coverClawback(issuer),
431 kLoanBrokerId(
keylet.key),
436 coverClawback(issuer),
437 kLoanBrokerId(
keylet.key),
443 coverClawback(issuer),
444 kLoanBrokerId(
keylet.key),
451 coverClawback(issuer),
452 kLoanBrokerId(
keylet.key),
462 verifyCoverAmount(0);
465 env(coverDeposit(alice,
keylet.key, vault.asset(6)));
467 verifyCoverAmount(6);
472 env(
set(alice, vault.vaultID), kLoanBrokerId(
keylet.key));
477 changeBroker(broker);
483 if (BEAST_EXPECT(broker) && checkChangedBroker)
484 checkChangedBroker(broker);
489 env(
set(alice, vault.vaultID),
490 kLoanBrokerId(broker->key()),
497 if (BEAST_EXPECT(broker))
499 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
500 BEAST_EXPECT(!broker->isFieldPresent(sfData));
510 env(del(alice, badKeylet.key));
515 auto const aliceBalance = env.balance(alice, vault.asset);
516 auto const coverFunds = env.balance(pseudoAccount, vault.asset);
517 BEAST_EXPECT(coverFunds.number() == broker->at(sfCoverAvailable));
519 verifyCoverAmount(6);
532 env(del(alice,
keylet.key));
536 BEAST_EXPECT(!broker);
537 auto pseudo = env.le(pseudoKeylet);
538 BEAST_EXPECT(!pseudo);
540 auto const expectedBalance = aliceBalance + coverFunds -
541 (aliceBalance.value().native() ?
STAmount(env.current()->fees().base.value())
558 Account const issuer{
"issuer"};
565 Account const bystander{
"bystander"};
573 env(
fset(issuer, asfAllowTrustLineClawback));
579 env(
trust(alice, iouAsset(1'000'000)));
580 env(
trust(evan, iouAsset(1'000'000)));
582 env(
pay(issuer, evan, iouAsset(100'000)));
583 env(
pay(issuer, alice, iouAsset(100'000)));
587 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
593 env(
pay(issuer, alice, mptAsset(100'000)));
594 env(
pay(issuer, evan, mptAsset(100'000)));
597 std::array const assets{xrpAsset, iouAsset, mptAsset};
601 for (
auto const& asset : assets)
603 auto [tx,
keylet] = vault.create({.owner = alice, .asset = asset});
611 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(50)}));
615 auto [tx,
keylet] = vault.create({.owner = alice, .asset = iouAsset});
620 return {iouAsset,
keylet.key, le->at(sfAccount)};
623 return {iouAsset,
keylet.key, evan.
id()};
626 auto const aliceOriginalCount = env.
ownerCount(alice);
629 for (
auto const& vault : vaults)
634 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
635 if (BEAST_EXPECT(accountInfo.isObject()))
637 auto const& accountData = accountInfo[jss::result][jss::account_data];
638 if (BEAST_EXPECT(accountData.isObject()))
640 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
641 BEAST_EXPECT(accountData[sfVaultID] ==
to_string(vault.vaultID));
643 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
644 if (BEAST_EXPECT(pseudoInfo.isObject()))
646 BEAST_EXPECT(pseudoInfo[jss::type] ==
"Vault");
666 env(
set(evan, vault.vaultID),
670 env(
set(evan, vault.vaultID),
674 env(
set(evan, vault.vaultID),
678 env(
set(evan, vault.vaultID),
683 env(
set(evan, vault.vaultID),
688 env(
set(evan, vault.vaultID),
693 env(
set(evan, vault.vaultID),
700 env(
set(evan, vault.vaultID),
701 kCoverRateMinimum(tenthBipsZero),
707 env(
set(evan, vault.vaultID),
709 kCoverRateLiquidation(tenthBipsZero),
732 BEAST_EXPECT(!broker->isFieldPresent(sfManagementFeeRate));
733 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateMinimum));
734 BEAST_EXPECT(!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);
741 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount + 4);
747 auto const nextKeylet =
756 env(
set(evan, vault.vaultID),
757 kLoanBrokerId(broker->key()),
760 env(
set(alice, vault.vaultID),
761 kLoanBrokerId(broker->key()),
765 env(
set(alice, vault.vaultID),
766 kLoanBrokerId(broker->key()),
770 env(
set(alice, vault.vaultID),
771 kLoanBrokerId(broker->key()),
776 testData =
"Test Data 1234";
778 env(
set(alice, vault.vaultID),
779 kLoanBrokerId(broker->key()),
784 env(
set(alice, vault.vaultID),
785 kLoanBrokerId(broker->key()),
786 kDebtMaximum(
Number(-175, -1)),
789 if (vault.asset.integral())
791 env(
set(alice, vault.vaultID),
792 kLoanBrokerId(broker->key()),
794 kDebtMaximum(debtMax),
799 env(
set(alice, vault.vaultID),
800 kLoanBrokerId(broker->key()),
802 kDebtMaximum(debtMax));
806 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
808 auto const actual = broker->at(sfDebtMaximum);
815 "non-default fields",
824 testData =
"spam spam spam spam";
837 BEAST_EXPECT(broker->at(sfManagementFeeRate) == 123);
838 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 100);
839 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 200);
840 BEAST_EXPECT(broker->at(sfDebtMaximum) ==
Number(9));
841 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
845 env(
set(alice, vault.vaultID),
846 kLoanBrokerId(broker->key()),
852 BEAST_EXPECT(!broker->isFieldPresent(sfData));
853 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
857 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount);
870 Account const issuer{
"issuer"};
873 Vault const vault{env};
875 env.
fund(
XRP(100'000), issuer, alice);
880 return getAsset(env, issuer, alice);
881 env(
trust(alice, issuer[
"IOU"](1'000'000)));
886 env(
pay(issuer, alice, asset(100'000)));
889 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
892 auto const le = env.
le(vaultKeylet);
894 if (BEAST_EXPECT(le))
895 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
901 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
904 auto const brokerKeylet =
909 auto broker = env.
le(brokerKeylet);
910 if (!BEAST_EXPECT(broker))
913 auto testZeroBrokerID = [&](
auto&& getTxJv) {
916 jv[sfLoanBrokerID] =
"";
924 auto testZeroVaultID = [&](
auto&& getTxJv) {
939 testZeroBrokerID([&]() {
return coverDeposit(alice, brokerKeylet.key, asset(10)); });
942 env(coverDeposit(alice, brokerKeylet.key, issuer[
"BAD"](10)),
Ter(
tecWRONG_ASSET));
945 env(
pay(alice, issuer, asset(100'000 - 50)));
947 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)),
954 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)));
961 testZeroBrokerID([&]() {
return coverWithdraw(alice, brokerKeylet.key, asset(10)); });
964 env(coverWithdraw(alice, brokerKeylet.key, issuer[
"BAD"](10)),
Ter(
tecWRONG_ASSET));
968 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
974 env.fund(
XRP(1'000), dest);
976 env(
fset(dest, asfRequireDest));
977 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
980 env(
fclear(dest, asfRequireDest));
983 env(
fset(dest, asfDepositAuth));
984 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
987 env(
fclear(dest, asfDepositAuth));
991 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
999 testZeroBrokerID([&]() {
1001 coverClawback(alice),
1002 kLoanBrokerId(brokerKeylet.key),
1009 env(coverClawback(issuer),
1010 kLoanBrokerId(brokerKeylet.key),
1015 env(
fset(issuer, asfAllowTrustLineClawback | asfNoFreeze));
1017 env(coverClawback(issuer),
1018 kLoanBrokerId(brokerKeylet.key),
1025 env(coverClawback(issuer),
1026 kLoanBrokerId(brokerKeylet.key),
1035 Account const borrower{
"borrower"};
1036 env.
fund(
XRP(1'000), borrower);
1037 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
1038 Sig(sfCounterpartySignature, alice),
1042 testZeroBrokerID([&]() {
return del(alice, brokerKeylet.key); });
1049 env(
loan::pay(borrower, loanKeylet.key, asset(50).value()));
1052 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
1055 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
1062 env(del(alice, brokerKeylet.key));
1068 testZeroBrokerID([&]() {
1069 return env.
json(
set(alice, vaultInfo.
vaultID), kLoanBrokerId(brokerKeylet.key));
1072 testZeroVaultID([&]() {
1073 return env.
json(
set(alice, vaultInfo.
vaultID), kLoanBrokerId(brokerKeylet.key));
1078 env(
fclear(issuer, asfDefaultRipple));
1083 env(
fset(issuer, asfDefaultRipple));
1089 env(
pay(alice, issuer, amt));
1099 testcase(
"Invalid LoanBrokerCoverClawback");
1100 using namespace jtx;
1106 Account const issuer{
"issuer"};
1107 auto const usd = alice[
"USD"];
1109 env.
fund(
XRP(100'000), alice);
1112 auto jtx = env.
jt(coverClawback(alice),
kAmount(usd(100)));
1119 jtx.jv[sfAmount] = bad.getJson();
1123 auto const jrr = env.
rpc(
"submit",
strHex(s.
slice()))[jss::result];
1125 BEAST_EXPECT(jrr[jss::error] ==
"invalidTransaction");
1126 BEAST_EXPECT(jrr[jss::error_exception] ==
"invalid native account");
1139 MPTTester const mpt({.env = env, .issuer = issuer, .holders = {alice}});
1148 testcase(
"Invalid LoanBrokerCoverDeposit");
1149 using namespace jtx;
1159 testcase(
"Invalid LoanBrokerCoverWithdraw");
1160 using namespace jtx;
1178 using namespace jtx;
1179 testcase(
"Invalid LoanBrokerDelete");
1192 using namespace jtx;
1210 using namespace jtx;
1219 Vault const vault{env};
1220 auto const [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1225 auto const brokerKeylet =
1233 STTx tx{ttLOAN_BROKER_COVER_DEPOSIT, [](
STObject&) {}};
1247 auto const vaultID = (*sleBroker)[sfVaultID];
1263 testcase(
"Require Auth - Implicit Pseudo-account authorization");
1264 using namespace jtx;
1267 Account const issuer{
"issuer"};
1272 env.
fund(
XRP(100'000), issuer, alice);
1279 .flags =
kMptDexFlags | tfMPTRequireAuth | tfMPTCanClawback | tfMPTCanLock,
1283 env(
pay(issuer, alice, asset(100'000)));
1287 asset.authorize({.account = issuer, .holder = alice, .flags = tfMPTUnauthorize});
1288 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1292 auto const le = env.
le(vaultKeylet);
1294 if (BEAST_EXPECT(le))
1295 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
1305 .flags = tfMPTUnauthorize,
1308 auto forUnauthAuth = [&](
auto&& doTx) {
1309 for (
auto const flag : {tfMPTUnauthorize, 0u})
1311 asset.authorize({.account = issuer, .holder = alice, .flags = flag});
1321 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(51)}),
1328 env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = asset(1)}),
1332 auto const brokerKeylet =
1335 forUnauthAuth([&](
auto) { env(
set(alice, vaultInfo.
vaultID)); });
1337 auto const broker = env.
le(brokerKeylet);
1338 if (!BEAST_EXPECT(broker))
1340 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
1345 .holder = brokerPseudo,
1346 .flags = tfMPTUnauthorize,
1352 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)), err);
1358 env(coverWithdraw(alice, brokerKeylet.key, vaultInfo.
asset(5)), err);
1362 forUnauthAuth([&](
bool) {
1363 env(coverClawback(issuer),
1364 kLoanBrokerId(brokerKeylet.key),
1372 testcase(
"testLoanBrokerSetDebtMaximum");
1373 using namespace jtx;
1375 Account const issuer{
"issuer"};
1378 Vault const vault{env};
1380 env.
fund(
XRP(100'000), issuer, alice);
1385 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1393 env(
pay(issuer, alice, asset(100'000)));
1396 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1399 auto const le = env.
le(vaultKeylet);
1401 if (BEAST_EXPECT(le))
1402 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
1408 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
1411 auto const brokerKeylet =
1416 Account const borrower{
"borrower"};
1417 env.
fund(
XRP(1'000), borrower);
1418 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
1419 Sig(sfCounterpartySignature, alice),
1421 auto const broker = env.
le(brokerKeylet);
1422 if (!BEAST_EXPECT(broker))
1425 BEAST_EXPECT(broker->at(sfDebtTotal) == 50);
1426 auto debtTotal = broker->at(sfDebtTotal);
1429 tx2[sfLoanBrokerID] =
to_string(brokerKeylet.key);
1430 tx2[sfDebtMaximum] = debtTotal - 1;
1433 tx2[sfDebtMaximum] = debtTotal + 1;
1436 tx2[sfDebtMaximum] = 0;
1443 auto const dm =
power(2, 64) - 1;
1445 tx2[sfDebtMaximum] = dm;
1452 tx2[sfDebtMaximum] =
"9223372036854775808";
1461 "invalidParamsField 'tx_json.DebtMaximum' has invalid data.");
1469 auto const dm =
power(2, 63);
1471 tx2[sfDebtMaximum] = dm;
1478 tx2[sfDebtMaximum] = dm;
1483 auto const dm =
power(2, 63) - 1;
1485 tx2[sfDebtMaximum] = dm;
1490 auto const dm =
power(2, 63) - 3;
1492 tx2[sfDebtMaximum] = dm;
1497 auto const dm =
power(2, 63) + 3;
1499 tx2[sfDebtMaximum] = dm;
1508 auto const dm =
power(2, 63) - 1;
1510 tx2[sfDebtMaximum] = dm;
1515 auto const dm =
power(2, 63) - 3;
1517 tx2[sfDebtMaximum] = dm;
1523 auto const dm = 2 * (
power(2, 62) - 1) + 1;
1525 tx2[sfDebtMaximum] = dm;
1529 tx2[sfDebtMaximum] =
Number{9223372036854775807, 0};
1537 using namespace jtx;
1538 Account const issuer(
"issuer");
1539 Account const holder(
"holder");
1540 Account const& broker = issuer;
1542 auto test = [&](
auto&& getToken) {
1545 env.
fund(
XRP(1'000), issuer, holder);
1550 Vault const vault(env);
1551 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
1555 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = deposit}),
1559 auto const brokerKeylet =
1571 auto const token = issuer[
"IOU"];
1596 for (
auto const& [
pay, max,
deposit, err] : mptTests)
1602 .holders = {holder},
1614 testcase <<
"RIPD-4466 - LoanBrokerSet disallows frozen vaults";
1615 using namespace jtx;
1618 Account const issuer{
"issuer"}, lender{
"lender"}, borrower{
"borrower"};
1619 env.
fund(
XRP(20'000), issuer, lender, borrower);
1620 auto const iou = issuer[
"IOU"];
1622 Vault const vault{env};
1623 auto [tx, vaultKeylet] = vault.create({.owner = lender, .asset = iou.asset()});
1628 auto const vaultSle = env.
le(vaultKeylet);
1629 auto const vaultPseudo = vaultSle->at(sfAccount);
1630 auto const vaultPseudoAcct =
Account(
"VaultPseudo", vaultPseudo);
1631 env(
trust(issuer, vaultPseudoAcct[
"IOU"](0), tfSetFreeze));
1639 testcase <<
"LoanBrokerDelete - locked broker pseudo-account MPT";
1640 using namespace jtx;
1643 Account const issuer(
"issuer");
1646 auto const withFix = features[fixCleanup3_2_0];
1647 Env env(*
this, features);
1648 env.
fund(
XRP(100'000), issuer, alice);
1653 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1660 env(
pay(issuer, alice, mpt(100'000)));
1664 Vault const vault{env};
1665 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
1670 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(10'000)}));
1674 auto const brokerKeylet =
1676 env(
set(alice, vaultKeylet.key));
1680 env(coverDeposit(alice, brokerKeylet.key, mpt(5'000).value()));
1684 auto const broker = env.
le(brokerKeylet);
1685 if (!BEAST_EXPECT(broker))
1687 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
1690 auto const brokerPseudoID = broker->at(sfAccount);
1694 auto const pseudoMpt = env.
le(pseudoMptKey);
1695 if (!BEAST_EXPECT(pseudoMpt))
1701 jv[jss::Account] = issuer.
human();
1703 jv[jss::Holder] =
toBase58(brokerPseudoID);
1704 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
1705 jv[jss::Flags] = tfMPTLock;
1712 auto const sle = env.
le(pseudoMptKey);
1713 if (!BEAST_EXPECT(sle))
1715 BEAST_EXPECT(sle->isFlag(lsfMPTLocked));
1719 auto const aliceBalanceBefore = env.
balance(alice, mpt);
1731 BEAST_EXPECT(env.
le(brokerKeylet) !=
nullptr);
1734 auto const aliceBalanceAfter = env.
balance(alice, mpt);
1735 BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore);
1738 BEAST_EXPECT(env.
le(pseudoMptKey) !=
nullptr);
1746 BEAST_EXPECT(env.
le(brokerKeylet) ==
nullptr);
1749 auto const aliceBalanceAfter = env.
balance(alice, mpt);
1750 BEAST_EXPECT(aliceBalanceAfter > aliceBalanceBefore);
1753 BEAST_EXPECT(env.
le(pseudoMptKey) ==
nullptr);
1760 testcase <<
"LoanBrokerDelete - frozen broker pseudo-account IOU";
1761 using namespace jtx;
1764 Account const issuer(
"issuer");
1767 auto const withFix = features[fixCleanup3_2_0];
1770 env.
fund(
XRP(100'000), issuer, alice);
1773 auto const iou = issuer[
"IOU"];
1776 env(
trust(alice, iou(1'000'000)));
1778 env(
pay(issuer, alice, iou(100'000)));
1782 Vault const vault{env};
1783 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = iou.asset()});
1788 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = iou(10'000)}));
1792 auto const brokerKeylet =
1794 env(
set(alice, vaultKeylet.key));
1798 env(coverDeposit(alice, brokerKeylet.key, iou(5'000)));
1802 auto const broker = env.
le(brokerKeylet);
1803 if (!BEAST_EXPECT(broker))
1805 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
1808 auto const brokerPseudoID = broker->at(sfAccount);
1809 auto const brokerPseudo =
Account(
"BrokerPseudo", brokerPseudoID);
1812 env(
trust(issuer, brokerPseudo[
"IOU"](0), tfSetFreeze));
1816 auto const aliceBalanceBefore = env.
balance(alice, iou);
1836 BEAST_EXPECT(env.
le(brokerKeylet) !=
nullptr);
1839 auto const aliceBalanceAfter = env.
balance(alice, iou);
1840 BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore);
1846 using namespace jtx;
1849 Account const issuer{
"issuer"};
1854 testcase(
"LoanBrokerCoverDeposit IOU freeze checks");
1856 Vault const vault{env};
1858 env.
fund(
XRP(100'000), issuer, alice);
1859 env(
trust(alice, issuer[
"IOU"](1'000'000)));
1862 env(
pay(issuer, alice, asset(100'000)));
1865 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1867 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
1870 auto const brokerKeylet =
1872 env(
set(alice, vaultKeylet.key));
1875 auto const broker = env.
le(brokerKeylet);
1876 if (!BEAST_EXPECT(broker))
1878 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
1880 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
1883 auto runTests = [&]() {
1884 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
1887 env(
fset(issuer, asfGlobalFreeze));
1888 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1889 env(
fclear(issuer, asfGlobalFreeze));
1892 env(
trust(issuer, asset(0), alice, tfSetFreeze));
1893 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1894 env(
trust(issuer, asset(0), alice, tfClearFreeze));
1897 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
1898 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1899 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
1903 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze));
1904 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(pseudoTer));
1905 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze));
1908 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze | tfSetDeepFreeze));
1909 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1910 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze | tfClearDeepFreeze));
1921 testcase(
"LoanBrokerCoverDeposit MPT lock checks");
1923 Vault const vault{env};
1925 env.
fund(
XRP(100'000), issuer, alice);
1929 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1933 env(
pay(issuer, alice, mpt(100'000)));
1936 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
1938 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(50)}));
1941 auto const brokerKeylet =
1943 env(
set(alice, vaultKeylet.key));
1946 auto const broker = env.
le(brokerKeylet);
1947 if (!BEAST_EXPECT(broker))
1949 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
1951 env(coverDeposit(alice, brokerKeylet.key, mpt(10)));
1956 auto runTests = [&]() {
1958 mptt.
set({.flags = tfMPTLock});
1960 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
1961 mptt.
set({.flags = tfMPTUnlock});
1965 mptt.
set({.holder = alice, .flags = tfMPTLock});
1967 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
1968 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
1972 mptt.
set({.holder = brokerPseudo, .flags = tfMPTLock});
1974 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
1975 mptt.
set({.holder = brokerPseudo, .flags = tfMPTUnlock});
1997 testcase(
"LoanBrokerCoverWithdraw IOU self-withdrawal while individually frozen");
1999 using namespace jtx;
2002 Account const issuer{
"issuer"};
2006 Vault const vault{env};
2008 env.
fund(
XRP(100'000), issuer, alice, dest);
2009 env(
trust(alice, issuer[
"IOU"](1'000'000)));
2010 env(
trust(dest, issuer[
"IOU"](1'000'000)));
2014 env(
pay(issuer, alice, asset(100'000)));
2017 auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
2019 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
2022 auto const brokerKeylet =
2024 env(
set(alice, vaultKeylet.key));
2027 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
2030 auto runTests = [&]() {
2031 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
2034 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2041 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tesSUCCESS));
2045 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2049 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2062 using namespace jtx;
2065 Account const issuer{
"issuer"};
2070 testcase(
"LoanBrokerCoverWithdraw IOU freeze checks");
2072 Vault const vault{env};
2074 env.
fund(
XRP(100'000), issuer, alice);
2075 env(
trust(alice, issuer[
"IOU"](1'000'000)));
2078 env(
pay(issuer, alice, asset(100'000)));
2081 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
2083 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
2086 auto const brokerKeylet =
2088 env(
set(alice, vaultKeylet.key));
2091 auto const broker = env.
le(brokerKeylet);
2092 if (!BEAST_EXPECT(broker))
2094 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2096 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
2101 env(
trust(dest, asset(1'000)));
2103 auto runTests = [&]() {
2104 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
2108 env(
fset(issuer, asfGlobalFreeze));
2109 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2112 env(
fclear(issuer, asfGlobalFreeze));
2115 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze));
2116 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2119 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze));
2122 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze | tfSetDeepFreeze));
2123 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2126 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze | tfClearDeepFreeze));
2129 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2130 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2134 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tesSUCCESS));
2135 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2137 alice, brokerKeylet.key, asset(
isTesSuccess(expectedTec) ? 2 : 1)));
2140 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
2141 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2145 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
2146 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
2148 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2151 env(
trust(issuer, asset(0), dest, tfSetFreeze));
2152 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2155 env(
trust(issuer, asset(0), dest, tfClearFreeze));
2156 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2159 env(
trust(issuer, asset(0), dest, tfSetFreeze | tfSetDeepFreeze));
2160 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2163 env(
trust(issuer, asset(0), dest, tfClearFreeze | tfClearDeepFreeze));
2166 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2167 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2168 kDestination(issuer),
2170 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2171 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2182 testcase(
"LoanBrokerCoverWithdraw MPT lock checks");
2184 Vault const vault{env};
2186 env.
fund(
XRP(100'000), issuer, alice);
2190 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
2194 env(
pay(issuer, alice, mpt(100'000)));
2197 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
2199 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(50)}));
2202 auto const brokerKeylet =
2204 env(
set(alice, vaultKeylet.key));
2207 auto const broker = env.
le(brokerKeylet);
2208 if (!BEAST_EXPECT(broker))
2210 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2212 env(coverDeposit(alice, brokerKeylet.key, mpt(10)));
2220 auto runTests = [&]() {
2221 auto const withFix = env.
current()->rules().enabled(fixCleanup3_3_0);
2228 mptt.
set({.flags = tfMPTLock});
2230 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2233 mptt.
set({.flags = tfMPTUnlock});
2237 mptt.
set({.holder = brokerPseudo, .flags = tfMPTLock});
2239 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2242 mptt.
set({.holder = brokerPseudo, .flags = tfMPTUnlock});
2246 mptt.
set({.holder = alice, .flags = tfMPTLock});
2248 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2250 Ter(submitterToDest));
2252 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
2253 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
2256 env(coverDeposit(alice, brokerKeylet.key, mpt(1)));
2260 mptt.
set({.holder = dest, .flags = tfMPTLock});
2262 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2265 mptt.
set({.holder = dest, .flags = tfMPTUnlock});
2269 mptt.
set({.holder = alice, .flags = tfMPTLock});
2271 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2272 kDestination(issuer),
2274 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
2275 env(coverDeposit(alice, brokerKeylet.key, mpt(1)));
2289 using namespace jtx;
2290 Account const issuer(
"broker");
2291 Account const broker(
"issuer");
2292 Account const dest(
"destination");
2293 auto const token = issuer[
"IOU"];
2295 enum class TrustState {
2303 auto test = [&](TrustState trustState) {
2306 testcase <<
"RIPD-4274 IOU with state: " <<
static_cast<int>(trustState);
2308 auto setTrustLine = [&](
Account const& acct, TrustState state) {
2311 case TrustState::RequireAuth:
2312 env(
trust(issuer,
token(0), acct, tfSetfAuth));
2314 case TrustState::ZeroLimit: {
2318 jv[sfQualityIn] = 10'000'000;
2322 case TrustState::ReachedLimit: {
2324 env(
pay(issuer, acct,
token(1'000)));
2328 case TrustState::NearLimit: {
2330 env(
pay(issuer, acct,
token(950)));
2334 case TrustState::NoTrustLine:
2338 BEAST_EXPECT(
false);
2343 env.
fund(
XRP(1'000), issuer, broker, dest);
2346 if (trustState == TrustState::RequireAuth)
2348 env(
fset(issuer, asfRequireAuth));
2351 setTrustLine(broker, TrustState::RequireAuth);
2354 setTrustLine(dest, trustState);
2357 env(
pay(issuer, broker,
token(2'000)));
2360 Vault const vault(env);
2361 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
2366 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2369 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
2375 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2378 auto const brokerKeylet =
2394 if (trustState == TrustState::RequireAuth)
2396 env(
fclear(issuer, asfRequireAuth));
2407 test(TrustState::RequireAuth);
2408 test(TrustState::ZeroLimit);
2409 test(TrustState::ReachedLimit);
2410 test(TrustState::NearLimit);
2411 test(TrustState::NoTrustLine);
2417 using namespace jtx;
2418 Account const issuer(
"broker");
2419 Account const broker(
"issuer");
2420 Account const dest(
"destination");
2422 enum class MPTState {
2428 auto test = [&](MPTState mptState) {
2431 testcase <<
"RIPD-4274 MPT with state: " <<
static_cast<int>(mptState);
2433 env.
fund(
XRP(1'000), issuer, broker, dest);
2439 case MPTState::RequireAuth: {
2443 .holders = {broker, dest},
2450 {.account = issuer, .holder = dest, .flags = tfMPTUnauthorize});
2453 case MPTState::ReachedMAX: {
2457 .holders = {broker, dest},
2461 BEAST_EXPECT(env.
balance(issuer, tester) == tester(-4'000));
2464 case MPTState::NoMPT: {
2468 .holders = {broker},
2474 return std::nullopt;
2477 if (!BEAST_EXPECT(maybeToken))
2480 auto const&
token = *maybeToken;
2482 Vault const vault(env);
2483 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
2488 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2491 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
2497 BEAST_EXPECT(env.
ter() == err);
2503 {.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2507 auto const brokerKeylet =
2519 BEAST_EXPECT(env.
ter() == err);
2523 test(MPTState::RequireAuth);
2524 test(MPTState::ReachedMAX);
2525 test(MPTState::NoMPT);
2542 using namespace jtx;
2545 Account const issuer{
"issuer"};
2555 Vault const vault{env};
2557 env.
fund(
XRP(100'000), issuer, alice);
2559 env(
fset(issuer, asfAllowTrustLineClawback));
2563 env(
trust(alice, iou(1'000'000)));
2565 env(
pay(issuer, alice, iou(1'000)));
2568 auto [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = iou});
2572 auto const brokerKeylet =
2574 env(
set(alice, vaultKeylet.key));
2577 env(coverDeposit(alice, brokerKeylet.key, iou(10)));
2580 return {brokerKeylet, iou};
2584 TER const expected =
2588 testcase(
"Cover precision guard: Deposit zero-at-scale");
2589 Env env{*
this, features};
2590 auto const [brokerKeylet, iou] = setup(env);
2592 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2593 env(coverDeposit(alice, brokerKeylet.key, subUlpAmt),
Ter(expected));
2597 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
2598 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
2603 testcase(
"Cover precision guard: Deposit rounds down");
2611 Env env{*
this, features};
2612 auto const [brokerKeylet, iou] = setup(env);
2614 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2615 env(coverDeposit(alice, brokerKeylet.key, subUlpAmt),
Ter(
tesSUCCESS));
2617 auto const brokerAfter = env.
le(brokerKeylet);
2618 if (!BEAST_EXPECT(brokerAfter))
2622 BEAST_EXPECT(brokerAfter->at(sfCoverAvailable) - coverBefore == delta);
2631 if (features[fixCleanup3_2_0])
2633 testcase(
"Cover precision guard: Deposit rounding bound");
2634 Env env{*
this, features};
2635 auto const [brokerKeylet, iou] = setup(env);
2636 Number const oneUlp{1, -14};
2643 auto const broker = env.
le(brokerKeylet);
2644 if (!BEAST_EXPECT(broker))
2646 Number const coverBefore = broker->at(sfCoverAvailable);
2647 env(coverDeposit(alice, brokerKeylet.key, iou(requested)),
Ter(
tesSUCCESS));
2649 auto const brokerAfter = env.
le(brokerKeylet);
2650 if (!BEAST_EXPECT(brokerAfter))
2652 Number const coverAfter = brokerAfter->at(sfCoverAvailable);
2653 Number const actual = coverAfter - coverBefore;
2654 Number const lost = requested - actual;
2655 BEAST_EXPECT(lost >=
Number{0});
2656 BEAST_EXPECT(lost < oneUlp);
2661 testcase(
"Cover precision guard: Withdraw");
2662 Env env{*
this, features};
2663 auto const [brokerKeylet, iou] = setup(env);
2665 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2666 auto const aliceBalanceBefore = env.
balance(alice, iou);
2667 env(coverWithdraw(alice, brokerKeylet.key, subUlpAmt),
Ter(expected));
2671 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
2672 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
2673 BEAST_EXPECT(env.
balance(alice, iou) == aliceBalanceBefore);
2678 testcase(
"Cover precision guard: Clawback");
2679 Env env{*
this, features};
2680 auto const [brokerKeylet, iou] = setup(env);
2682 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2683 env(coverClawback(issuer),
2684 kLoanBrokerId(brokerKeylet.key),
2690 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
2691 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
2698 testcase(
"Cover precision guard: MPT min amount passes");
2701 env.
fund(
XRP(100'000), issuer, alice);
2705 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
2712 env(
pay(issuer, alice, mptAsset(100)));
2715 Vault const vault{env};
2716 auto [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = mptAsset});
2720 auto const brokerKeylet =
2722 env(
set(alice, vaultKeylet.key));
2725 env(coverDeposit(alice, brokerKeylet.key, mptAsset(10)));
2728 env(coverDeposit(alice, brokerKeylet.key, mptAsset(1)),
Ter(
tesSUCCESS));
2731 env(coverWithdraw(alice, brokerKeylet.key, mptAsset(1)),
Ter(
tesSUCCESS));
2734 env(coverClawback(issuer),
2735 kLoanBrokerId(brokerKeylet.key),
2743 runTestCases(
all_ - fixCleanup3_2_0);
A generic endpoint for log messages.
void fail(String const &reason, char const *file, int line)
Record a failure.
TestcaseT testcase
Memberspace for declaring test cases.
static constexpr Int kMaxInt
State information when applying a tx.
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(SLE::ref sle)=0
Remove a peeked SLE.
A currency issued by an account.
static TER preclaim(PreclaimContext const &ctx)
Number is a floating point type that can represent a wide range of values.
static MantissaRange::MantissaScale getMantissaScale()
Returns which mantissa scale is currently in use for normalization.
Writable ledger view that accumulates state and tx changes.
std::shared_ptr< STLedgerEntry const > const & const_ref
void setFieldAmount(SField const &field, STAmount const &)
void setAccountID(SField const &field, AccountID const &)
void setFieldH256(SField const &field, uint256 const &)
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Slice slice() const noexcept
void testInvalidLoanBrokerSet()
void lifecycle(char const *label, jtx::Env &env, jtx::Account const &issuer, jtx::Account const &alice, jtx::Account const &evan, jtx::Account const &bystander, VaultInfo const &vault, VaultInfo const &badVault, std::function< jtx::JTx(jtx::JTx const &)> modifyJTx, std::function< void(SLE::const_ref)> checkBroker, std::function< void(SLE::const_ref)> changeBroker, std::function< void(SLE::const_ref)> checkChangedBroker)
void testLoanBrokerDeleteFrozenIOU(FeatureBitset features)
void run() override
Runs the suite.
void testCoverPrecisionGuard()
void testLoanBroker(std::function< jtx::PrettyAsset(jtx::Env &, jtx::Account const &, jtx::Account const &)> getAsset, LoanBrokerTest brokerTest)
void testInvalidLoanBrokerCoverClawback()
void testInvalidLoanBrokerCoverWithdraw()
void testCoverWithdrawSelfWhileFrozen()
void testInvalidLoanBrokerCoverDeposit()
void testCoverDepositFreezes()
void testCoverWithdrawFreezes()
void testLoanBrokerSetDebtMaximum()
void testAmB06VaultFreezeCheckMissing()
void testLoanBrokerCoverDepositNullVault()
void testLoanBrokerDeleteLockedMPT(FeatureBitset features)
void testInvalidLoanBrokerDelete()
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
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.
TER ter() const
Return the TER for the last JTx.
json::Value json(JsonValue &&jv, FN const &... fN)
Create JSON from parameters.
SLE::const_pointer le(Account const &account) const
Return an account root.
std::uint32_t ownerCount(Account const &account) const
Return the number of objects owned by an account.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
void enableFeature(uint256 const feature)
void disableFeature(uint256 const feature)
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.
void setParseFailureExpected(bool b)
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.
std::shared_ptr< STTx const > ust(JTx const &jt)
Create a STTx from a JTx without sanitizing Use to inject bogus values into test transactions by firs...
beast::Journal const journal
void require(Args const &... args)
Check a set of requirements.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
void set(MPTSet const &set={})
void create(MPTCreate const &arg=MPTCreate{})
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
MPTID const & issuanceID() const
Converts to MPT Issue or STAmount.
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.
T emplace_back(T... args)
Keylet computation functions.
Keylet loan(uint256 const &loanBrokerID, SeqProxy const &loanSeq) noexcept
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet loanBroker(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
constexpr TenthBips32 kMaxCoverRate
The maximum coverage rate required of a loan broker in 1/10 bips.
constexpr TenthBips16 kMaxManagementFeeRate(unsafeCast< std::uint16_t >(percentageToTenthBips(10).value()))
The maximum management fee rate allowed by a loan broker in 1/10 bips.
Deposit preauthorize operations.
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 coverWithdraw(AccountID const &account, uint256 const &brokerID, STAmount const &amount, uint32_t flags)
json::Value set(AccountID const &account, uint256 const &loanBrokerID, Number principalRequested, std::uint32_t flags)
json::Value del(AccountID const &account, uint256 const &loanID, std::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.
XrpT const XRP
Converts to XRP Issue or STAmount.
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
FeatureBitset testableAmendments()
auto const kData
General field definitions, or fields used in multiple transaction namespaces.
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
bool checkVL(Slice const &result, std::string const &expected)
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
static MPTInit const kMptInitNoFund
static STAmount accountReserve(jtx::Env &env, std::uint32_t count=1)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
STTx createTx(bool disabling, LedgerIndex seq, PublicKey const &txKey)
Create ttUNL_MODIFY Tx.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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.
std::string strHex(FwdIt begin, FwdIt end)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
Number power(Number const &f, unsigned n)
TenthBips< std::uint32_t > TenthBips32
TenthBips< std::uint16_t > TenthBips16
constexpr FlagValue tfUniversal
std::string to_string(BaseUInt< Bits, Tag > const &a)
constexpr std::size_t kMaxDataPayloadLength
The maximum length of Data payload.
Asset getAsset(T const &amt)
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
@ tecINSUFFICIENT_RESERVE
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
constexpr FlagValue tfFullyCanonicalSig
State information when determining if a tx is likely to claim a fee.
jtx::Account pseudoAccount
VaultInfo(jtx::PrettyAsset const &asset, uint256 const &vaultId, AccountID const &pseudo)
Set the destination tag on a JTx.
Execution context for applying a JSON transaction.
Represents an XRP, IOU, or MPT quantity This customizes the string conversion and supports XRP conver...
Set the sequence number on a JTx.