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/credentials.h>
10#include <test/jtx/deposit.h>
11#include <test/jtx/fee.h>
12#include <test/jtx/flags.h>
13#include <test/jtx/mpt.h>
14#include <test/jtx/pay.h>
15#include <test/jtx/seq.h>
16#include <test/jtx/sig.h>
17#include <test/jtx/tag.h>
18#include <test/jtx/tags.h>
19#include <test/jtx/ter.h>
20#include <test/jtx/trust.h>
21#include <test/jtx/txflags.h>
22#include <test/jtx/vault.h>
23#include <test/unit_test/SuiteJournal.h>
25#include <xrpl/basics/Number.h>
26#include <xrpl/basics/base_uint.h>
27#include <xrpl/basics/strHex.h>
28#include <xrpl/beast/unit_test/suite.h>
29#include <xrpl/beast/utility/Journal.h>
30#include <xrpl/beast/utility/Zero.h>
31#include <xrpl/core/ServiceRegistry.h>
32#include <xrpl/ledger/ApplyView.h>
33#include <xrpl/ledger/OpenView.h>
34#include <xrpl/ledger/helpers/AccountRootHelpers.h>
35#include <xrpl/protocol/AccountID.h>
36#include <xrpl/protocol/Feature.h>
37#include <xrpl/protocol/Indexes.h>
38#include <xrpl/protocol/Issue.h>
39#include <xrpl/protocol/LedgerFormats.h>
40#include <xrpl/protocol/MPTIssue.h>
41#include <xrpl/protocol/Protocol.h>
42#include <xrpl/protocol/SField.h>
43#include <xrpl/protocol/STAmount.h>
44#include <xrpl/protocol/STObject.h>
45#include <xrpl/protocol/STTx.h>
46#include <xrpl/protocol/SeqProxy.h>
47#include <xrpl/protocol/Serializer.h>
48#include <xrpl/protocol/TER.h>
49#include <xrpl/protocol/TxFlags.h>
50#include <xrpl/protocol/TxFormats.h>
51#include <xrpl/protocol/Units.h>
52#include <xrpl/protocol/jss.h>
53#include <xrpl/tx/ApplyContext.h>
54#include <xrpl/tx/Transactor.h>
55#include <xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h>
92 auto failAll = [
this](
FeatureBitset features,
bool goodVault =
false) {
93 Env env(*
this, features);
100 Vault const vault{env};
101 auto const [tx,
keylet] = vault.create({.owner = alice, .asset = asset});
104 BEAST_EXPECT(
static_cast<bool>(env.
le(
keylet)) == goodVault);
109 auto const brokerKeylet =
113 env(coverDeposit(alice, brokerKeylet.key, asset(1000)),
Ter(
temDISABLED));
115 env(coverWithdraw(alice, brokerKeylet.key, asset(1000)),
Ter(
temDISABLED));
118 env(coverClawback(alice), kLoanBrokerId(brokerKeylet.key),
Ter(
temDISABLED));
120 env(coverClawback(alice),
121 kLoanBrokerId(brokerKeylet.key),
127 failAll(
all_ - featureMPTokensV1);
128 failAll(
all_ - featureSingleAssetVault - featureLendingProtocol);
129 failAll(
all_ - featureSingleAssetVault);
130 failAll(
all_ - featureLendingProtocol,
true);
160 auto const& asset = vault.asset.raw();
166 else if (asset.holds<
Issue>())
176 assetLabel =
"Unknown ";
178 testcase <<
"Lifecycle: " << assetLabel << label;
187 badMptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
189 return badMptt[
"BAD"];
191 static PrettyAsset const kBadIouAsset = evan[
"BAD"];
192 static Account const kNonExistent{
"NonExistent"};
193 static PrettyAsset const kGhostIouAsset = kNonExistent[
"GST"];
194 PrettyAsset const vaultPseudoIouAsset = vault.pseudoAccount[
"PSD"];
196 auto const badKeylet =
200 auto const badBrokerPseudo = [&]() {
201 if (
auto const le = env.
le(badKeylet); BEAST_EXPECT(le))
203 return Account{
"Bad Broker pseudo-account", le->at(sfAccount)};
206 return vault.pseudoAccount;
208 PrettyAsset const badBrokerPseudoIouAsset = badBrokerPseudo[
"WAT"];
213 auto jtx = env.
jt(
set(alice, vault.vaultID));
222 if (
auto broker = env.
le(
keylet); BEAST_EXPECT(broker))
226 BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
227 BEAST_EXPECT(broker->at(sfAccount) != alice.
id());
228 BEAST_EXPECT(broker->at(sfOwner) == alice.
id());
229 BEAST_EXPECT(broker->at(sfFlags) == 0);
230 BEAST_EXPECT(broker->at(sfSequence) == env.
seq(alice) - 1);
231 BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
232 BEAST_EXPECT(broker->at(sfLoanSequence) == 1);
233 BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
234 BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
244 Account const pseudoAccount{
"Broker pseudo-account", broker->at(sfAccount)};
247 if (
auto const pseudo = env.
le(pseudoKeylet); BEAST_EXPECT(pseudo))
253 pseudo->at(sfFlags) == (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth));
254 BEAST_EXPECT(pseudo->at(sfSequence) == 0);
256 BEAST_EXPECT(pseudo->at(sfOwnerCount) == (vault.asset.raw().native() ? 0 : 1));
257 BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
258 BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
259 BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
260 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
261 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
262 BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
263 BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
264 BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
265 BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
266 BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
267 BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
268 BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
269 BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
270 BEAST_EXPECT(!pseudo->isFieldPresent(sfFirstNFTokenSequence));
271 BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
272 BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
273 BEAST_EXPECT(pseudo->at(sfLoanBrokerID) ==
keylet.key);
279 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
280 if (BEAST_EXPECT(accountInfo.isObject()))
282 auto const& accountData = accountInfo[jss::result][jss::account_data];
283 if (BEAST_EXPECT(accountData.isObject()))
285 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
288 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
289 if (BEAST_EXPECT(pseudoInfo.isObject()))
291 BEAST_EXPECT(pseudoInfo[jss::type] ==
"LoanBroker");
296 auto verifyCoverAmount =
297 [&env, &vault, &pseudoAccount, &broker, &
keylet,
this](
auto n) {
301 if (BEAST_EXPECT(broker))
303 auto const amount = vault.asset(n);
304 BEAST_EXPECT(broker->at(sfCoverAvailable) == amount.number());
314 env(coverDeposit(alice, vault.vaultID, vault.asset(10)),
Ter(
tecNO_ENTRY));
316 verifyCoverAmount(0);
326 env(coverClawback(evan), kLoanBrokerId(vault.vaultID),
Ter(
tecNO_ENTRY));
329 if (vault.asset.raw().native())
336 if (vault.asset.raw().holds<
Issue>())
343 env(coverClawback(alice),
344 kAmount(vaultPseudoIouAsset(1)),
348 env(coverClawback(issuer),
349 kLoanBrokerId(
keylet.key),
350 kAmount(badBrokerPseudoIouAsset(10)),
352 PrettyAsset const brokerWrongCurrencyAsset = pseudoAccount[
"WAT"];
353 env(coverClawback(issuer),
354 kLoanBrokerId(
keylet.key),
355 kAmount(brokerWrongCurrencyAsset(10)),
362 BEAST_EXPECT(vault.asset.raw().holds<
MPTIssue>());
367 env(coverClawback(issuer),
368 kLoanBrokerId(
keylet.key),
375 env(coverDeposit(alice,
keylet.key, vault.asset(10)));
377 verifyCoverAmount(10);
384 env(coverWithdraw(alice, vault.vaultID, vault.asset(10)),
Ter(
tecNO_ENTRY));
388 if (!vault.asset.raw().native())
391 env(coverWithdraw(alice,
keylet.key, vault.asset(1)),
392 kDestination(bystander),
397 env(coverWithdraw(alice,
keylet.key, vault.asset(1)),
402 env(coverWithdraw(alice,
keylet.key, vault.asset(7)));
404 verifyCoverAmount(3);
407 env(coverDeposit(alice,
keylet.key, vault.asset(5)));
409 verifyCoverAmount(8);
413 env(coverWithdraw(alice,
keylet.key, vault.asset(1)), kDestination(evan));
415 verifyCoverAmount(7);
419 env(coverWithdraw(alice,
keylet.key, vault.asset(1)), kDestination(evan),
Dtag(3));
421 verifyCoverAmount(6);
423 if (!vault.asset.raw().native())
426 env(coverClawback(issuer), kLoanBrokerId(
keylet.key),
kAmount(vault.asset(2)));
428 verifyCoverAmount(4);
431 env(coverDeposit(alice,
keylet.key, vault.asset(5)));
433 verifyCoverAmount(9);
436 for (
auto const& tx : {
439 coverClawback(issuer),
440 kLoanBrokerId(
keylet.key),
445 coverClawback(issuer),
446 kLoanBrokerId(
keylet.key),
452 coverClawback(issuer),
453 kLoanBrokerId(
keylet.key),
460 coverClawback(issuer),
461 kLoanBrokerId(
keylet.key),
471 verifyCoverAmount(0);
474 env(coverDeposit(alice,
keylet.key, vault.asset(6)));
476 verifyCoverAmount(6);
481 env(
set(alice, vault.vaultID), kLoanBrokerId(
keylet.key));
486 changeBroker(broker);
492 if (BEAST_EXPECT(broker) && checkChangedBroker)
493 checkChangedBroker(broker);
498 env(
set(alice, vault.vaultID),
499 kLoanBrokerId(broker->key()),
506 if (BEAST_EXPECT(broker))
508 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
509 BEAST_EXPECT(!broker->isFieldPresent(sfData));
519 env(del(alice, badKeylet.key));
524 auto const aliceBalance = env.balance(alice, vault.asset);
525 auto const coverFunds = env.balance(pseudoAccount, vault.asset);
526 BEAST_EXPECT(coverFunds.number() == broker->at(sfCoverAvailable));
528 verifyCoverAmount(6);
541 env(del(alice,
keylet.key));
545 BEAST_EXPECT(!broker);
546 auto pseudo = env.le(pseudoKeylet);
547 BEAST_EXPECT(!pseudo);
549 auto const expectedBalance = aliceBalance + coverFunds -
550 (aliceBalance.value().native() ?
STAmount(env.current()->fees().base.value())
567 Account const issuer{
"issuer"};
574 Account const bystander{
"bystander"};
582 env(
fset(issuer, asfAllowTrustLineClawback));
588 env(
trust(alice, iouAsset(1'000'000)));
589 env(
trust(evan, iouAsset(1'000'000)));
591 env(
pay(issuer, evan, iouAsset(100'000)));
592 env(
pay(issuer, alice, iouAsset(100'000)));
596 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
602 env(
pay(issuer, alice, mptAsset(100'000)));
603 env(
pay(issuer, evan, mptAsset(100'000)));
606 std::array const assets{xrpAsset, iouAsset, mptAsset};
610 for (
auto const& asset : assets)
612 auto [tx,
keylet] = vault.create({.owner = alice, .asset = asset});
620 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(50)}));
624 auto [tx,
keylet] = vault.create({.owner = alice, .asset = iouAsset});
629 return {iouAsset,
keylet.key, le->at(sfAccount)};
632 return {iouAsset,
keylet.key, evan.
id()};
635 auto const aliceOriginalCount = env.
ownerCount(alice);
638 for (
auto const& vault : vaults)
643 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
644 if (BEAST_EXPECT(accountInfo.isObject()))
646 auto const& accountData = accountInfo[jss::result][jss::account_data];
647 if (BEAST_EXPECT(accountData.isObject()))
649 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
650 BEAST_EXPECT(accountData[sfVaultID] ==
to_string(vault.vaultID));
652 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
653 if (BEAST_EXPECT(pseudoInfo.isObject()))
655 BEAST_EXPECT(pseudoInfo[jss::type] ==
"Vault");
675 env(
set(evan, vault.vaultID),
679 env(
set(evan, vault.vaultID),
683 env(
set(evan, vault.vaultID),
687 env(
set(evan, vault.vaultID),
692 env(
set(evan, vault.vaultID),
697 env(
set(evan, vault.vaultID),
702 env(
set(evan, vault.vaultID),
709 env(
set(evan, vault.vaultID),
710 kCoverRateMinimum(tenthBipsZero),
716 env(
set(evan, vault.vaultID),
718 kCoverRateLiquidation(tenthBipsZero),
741 BEAST_EXPECT(!broker->isFieldPresent(sfManagementFeeRate));
742 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateMinimum));
743 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateLiquidation));
744 BEAST_EXPECT(!broker->isFieldPresent(sfData));
745 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
746 BEAST_EXPECT(broker->at(sfDebtMaximum) == 0);
747 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 0);
748 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 0);
750 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount + 4);
756 auto const nextKeylet =
765 env(
set(evan, vault.vaultID),
766 kLoanBrokerId(broker->key()),
769 env(
set(alice, vault.vaultID),
770 kLoanBrokerId(broker->key()),
774 env(
set(alice, vault.vaultID),
775 kLoanBrokerId(broker->key()),
779 env(
set(alice, vault.vaultID),
780 kLoanBrokerId(broker->key()),
785 testData =
"Test Data 1234";
787 env(
set(alice, vault.vaultID),
788 kLoanBrokerId(broker->key()),
793 env(
set(alice, vault.vaultID),
794 kLoanBrokerId(broker->key()),
795 kDebtMaximum(
Number(-175, -1)),
798 if (vault.asset.integral())
800 env(
set(alice, vault.vaultID),
801 kLoanBrokerId(broker->key()),
803 kDebtMaximum(debtMax),
808 env(
set(alice, vault.vaultID),
809 kLoanBrokerId(broker->key()),
811 kDebtMaximum(debtMax));
815 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
817 auto const actual = broker->at(sfDebtMaximum);
824 "non-default fields",
833 testData =
"spam spam spam spam";
846 BEAST_EXPECT(broker->at(sfManagementFeeRate) == 123);
847 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 100);
848 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 200);
849 BEAST_EXPECT(broker->at(sfDebtMaximum) ==
Number(9));
850 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
854 env(
set(alice, vault.vaultID),
855 kLoanBrokerId(broker->key()),
861 BEAST_EXPECT(!broker->isFieldPresent(sfData));
862 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
866 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount);
879 Account const issuer{
"issuer"};
882 Vault const vault{env};
884 env.
fund(
XRP(100'000), issuer, alice);
889 return getAsset(env, issuer, alice);
890 env(
trust(alice, issuer[
"IOU"](1'000'000)));
895 env(
pay(issuer, alice, asset(100'000)));
898 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
901 auto const le = env.
le(vaultKeylet);
903 if (BEAST_EXPECT(le))
904 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
910 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
913 auto const brokerKeylet =
918 auto broker = env.
le(brokerKeylet);
919 if (!BEAST_EXPECT(broker))
922 auto testZeroBrokerID = [&](
auto&& getTxJv) {
925 jv[sfLoanBrokerID] =
"";
933 auto testZeroVaultID = [&](
auto&& getTxJv) {
948 testZeroBrokerID([&]() {
return coverDeposit(alice, brokerKeylet.key, asset(10)); });
951 env(coverDeposit(alice, brokerKeylet.key, issuer[
"BAD"](10)),
Ter(
tecWRONG_ASSET));
954 env(
pay(alice, issuer, asset(100'000 - 50)));
956 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)),
963 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)));
970 testZeroBrokerID([&]() {
return coverWithdraw(alice, brokerKeylet.key, asset(10)); });
973 env(coverWithdraw(alice, brokerKeylet.key, issuer[
"BAD"](10)),
Ter(
tecWRONG_ASSET));
977 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
983 env.fund(
XRP(1'000), dest);
985 env(
fset(dest, asfRequireDest));
986 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
989 env(
fclear(dest, asfRequireDest));
992 env(
fset(dest, asfDepositAuth));
993 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
996 env(
fclear(dest, asfDepositAuth));
1000 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
1008 testZeroBrokerID([&]() {
1010 coverClawback(alice),
1011 kLoanBrokerId(brokerKeylet.key),
1018 env(coverClawback(issuer),
1019 kLoanBrokerId(brokerKeylet.key),
1024 env(
fset(issuer, asfAllowTrustLineClawback | asfNoFreeze));
1026 env(coverClawback(issuer),
1027 kLoanBrokerId(brokerKeylet.key),
1034 env(coverClawback(issuer),
1035 kLoanBrokerId(brokerKeylet.key),
1044 Account const borrower{
"borrower"};
1045 env.
fund(
XRP(1'000), borrower);
1046 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
1047 Sig(sfCounterpartySignature, alice),
1051 testZeroBrokerID([&]() {
return del(alice, brokerKeylet.key); });
1058 env(
loan::pay(borrower, loanKeylet.key, asset(50).value()));
1061 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
1064 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
1071 env(del(alice, brokerKeylet.key));
1077 testZeroBrokerID([&]() {
1078 return env.
json(
set(alice, vaultInfo.
vaultID), kLoanBrokerId(brokerKeylet.key));
1081 testZeroVaultID([&]() {
1082 return env.
json(
set(alice, vaultInfo.
vaultID), kLoanBrokerId(brokerKeylet.key));
1087 env(
fclear(issuer, asfDefaultRipple));
1092 env(
fset(issuer, asfDefaultRipple));
1098 env(
pay(alice, issuer, amt));
1108 testcase(
"Invalid LoanBrokerCoverClawback");
1109 using namespace jtx;
1115 Account const issuer{
"issuer"};
1116 auto const usd = alice[
"USD"];
1118 env.
fund(
XRP(100'000), alice);
1121 auto jtx = env.
jt(coverClawback(alice),
kAmount(usd(100)));
1128 jtx.jv[sfAmount] = bad.getJson();
1132 auto const jrr = env.
rpc(
"submit",
strHex(s.
slice()))[jss::result];
1134 BEAST_EXPECT(jrr[jss::error] ==
"invalidTransaction");
1135 BEAST_EXPECT(jrr[jss::error_exception] ==
"invalid native account");
1148 MPTTester const mpt({.env = env, .issuer = issuer, .holders = {alice}});
1157 testcase(
"Invalid LoanBrokerCoverDeposit");
1158 using namespace jtx;
1168 testcase(
"Invalid LoanBrokerCoverWithdraw");
1169 using namespace jtx;
1187 using namespace jtx;
1188 testcase(
"Invalid LoanBrokerDelete");
1201 using namespace jtx;
1219 using namespace jtx;
1228 Vault const vault{env};
1229 auto const [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1234 auto const brokerKeylet =
1242 STTx tx{ttLOAN_BROKER_COVER_DEPOSIT, [](
STObject&) {}};
1256 auto const vaultID = (*sleBroker)[sfVaultID];
1272 testcase(
"Require Auth - Implicit Pseudo-account authorization");
1273 using namespace jtx;
1276 Account const issuer{
"issuer"};
1281 env.
fund(
XRP(100'000), issuer, alice);
1288 .flags =
kMptDexFlags | tfMPTRequireAuth | tfMPTCanClawback | tfMPTCanLock,
1292 env(
pay(issuer, alice, asset(100'000)));
1296 asset.authorize({.account = issuer, .holder = alice, .flags = tfMPTUnauthorize});
1297 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1301 auto const le = env.
le(vaultKeylet);
1303 if (BEAST_EXPECT(le))
1304 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
1314 .flags = tfMPTUnauthorize,
1317 auto forUnauthAuth = [&](
auto&& doTx) {
1318 for (
auto const flag : {tfMPTUnauthorize, 0u})
1320 asset.authorize({.account = issuer, .holder = alice, .flags = flag});
1330 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(51)}),
1337 env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = asset(1)}),
1341 auto const brokerKeylet =
1344 forUnauthAuth([&](
auto) { env(
set(alice, vaultInfo.
vaultID)); });
1346 auto const broker = env.
le(brokerKeylet);
1347 if (!BEAST_EXPECT(broker))
1349 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
1354 .holder = brokerPseudo,
1355 .flags = tfMPTUnauthorize,
1361 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)), err);
1367 env(coverWithdraw(alice, brokerKeylet.key, vaultInfo.
asset(5)), err);
1371 forUnauthAuth([&](
bool) {
1372 env(coverClawback(issuer),
1373 kLoanBrokerId(brokerKeylet.key),
1381 testcase(
"testLoanBrokerSetDebtMaximum");
1382 using namespace jtx;
1384 Account const issuer{
"issuer"};
1387 Vault const vault{env};
1389 env.
fund(
XRP(100'000), issuer, alice);
1394 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1402 env(
pay(issuer, alice, asset(100'000)));
1405 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1408 auto const le = env.
le(vaultKeylet);
1410 if (BEAST_EXPECT(le))
1411 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
1417 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
1420 auto const brokerKeylet =
1425 Account const borrower{
"borrower"};
1426 env.
fund(
XRP(1'000), borrower);
1427 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
1428 Sig(sfCounterpartySignature, alice),
1430 auto const broker = env.
le(brokerKeylet);
1431 if (!BEAST_EXPECT(broker))
1434 BEAST_EXPECT(broker->at(sfDebtTotal) == 50);
1435 auto debtTotal = broker->at(sfDebtTotal);
1438 tx2[sfLoanBrokerID] =
to_string(brokerKeylet.key);
1439 tx2[sfDebtMaximum] = debtTotal - 1;
1442 tx2[sfDebtMaximum] = debtTotal + 1;
1445 tx2[sfDebtMaximum] = 0;
1452 auto const dm =
power(2, 64) - 1;
1454 tx2[sfDebtMaximum] = dm;
1461 tx2[sfDebtMaximum] =
"9223372036854775808";
1470 "invalidParamsField 'tx_json.DebtMaximum' has invalid data.");
1478 auto const dm =
power(2, 63);
1480 tx2[sfDebtMaximum] = dm;
1487 tx2[sfDebtMaximum] = dm;
1492 auto const dm =
power(2, 63) - 1;
1494 tx2[sfDebtMaximum] = dm;
1499 auto const dm =
power(2, 63) - 3;
1501 tx2[sfDebtMaximum] = dm;
1506 auto const dm =
power(2, 63) + 3;
1508 tx2[sfDebtMaximum] = dm;
1517 auto const dm =
power(2, 63) - 1;
1519 tx2[sfDebtMaximum] = dm;
1524 auto const dm =
power(2, 63) - 3;
1526 tx2[sfDebtMaximum] = dm;
1532 auto const dm = 2 * (
power(2, 62) - 1) + 1;
1534 tx2[sfDebtMaximum] = dm;
1538 tx2[sfDebtMaximum] =
Number{9223372036854775807, 0};
1546 using namespace jtx;
1547 Account const issuer(
"issuer");
1548 Account const holder(
"holder");
1549 Account const& broker = issuer;
1551 auto test = [&](
auto&& getToken) {
1554 env.
fund(
XRP(1'000), issuer, holder);
1559 Vault const vault(env);
1560 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
1564 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = deposit}),
1568 auto const brokerKeylet =
1580 auto const token = issuer[
"IOU"];
1605 for (
auto const& [
pay, max,
deposit, err] : mptTests)
1611 .holders = {holder},
1623 testcase <<
"RIPD-4466 - LoanBrokerSet disallows frozen vaults";
1624 using namespace jtx;
1627 Account const issuer{
"issuer"}, lender{
"lender"}, borrower{
"borrower"};
1628 env.
fund(
XRP(20'000), issuer, lender, borrower);
1629 auto const iou = issuer[
"IOU"];
1631 Vault const vault{env};
1632 auto [tx, vaultKeylet] = vault.create({.owner = lender, .asset = iou.asset()});
1637 auto const vaultSle = env.
le(vaultKeylet);
1638 auto const vaultPseudo = vaultSle->at(sfAccount);
1639 auto const vaultPseudoAcct =
Account(
"VaultPseudo", vaultPseudo);
1640 env(
trust(issuer, vaultPseudoAcct[
"IOU"](0), tfSetFreeze));
1648 testcase <<
"LoanBrokerDelete - locked broker pseudo-account MPT";
1649 using namespace jtx;
1652 Account const issuer(
"issuer");
1655 auto const withFix = features[fixCleanup3_2_0];
1656 Env env(*
this, features);
1657 env.
fund(
XRP(100'000), issuer, alice);
1662 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1669 env(
pay(issuer, alice, mpt(100'000)));
1673 Vault const vault{env};
1674 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
1679 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(10'000)}));
1683 auto const brokerKeylet =
1685 env(
set(alice, vaultKeylet.key));
1689 env(coverDeposit(alice, brokerKeylet.key, mpt(5'000).value()));
1693 auto const broker = env.
le(brokerKeylet);
1694 if (!BEAST_EXPECT(broker))
1696 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
1699 auto const brokerPseudoID = broker->at(sfAccount);
1703 auto const pseudoMpt = env.
le(pseudoMptKey);
1704 if (!BEAST_EXPECT(pseudoMpt))
1710 jv[jss::Account] = issuer.
human();
1712 jv[jss::Holder] =
toBase58(brokerPseudoID);
1713 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
1714 jv[jss::Flags] = tfMPTLock;
1721 auto const sle = env.
le(pseudoMptKey);
1722 if (!BEAST_EXPECT(sle))
1724 BEAST_EXPECT(sle->isFlag(lsfMPTLocked));
1728 auto const aliceBalanceBefore = env.
balance(alice, mpt);
1740 BEAST_EXPECT(env.
le(brokerKeylet) !=
nullptr);
1743 auto const aliceBalanceAfter = env.
balance(alice, mpt);
1744 BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore);
1747 BEAST_EXPECT(env.
le(pseudoMptKey) !=
nullptr);
1755 BEAST_EXPECT(env.
le(brokerKeylet) ==
nullptr);
1758 auto const aliceBalanceAfter = env.
balance(alice, mpt);
1759 BEAST_EXPECT(aliceBalanceAfter > aliceBalanceBefore);
1762 BEAST_EXPECT(env.
le(pseudoMptKey) ==
nullptr);
1773 testcase <<
"LoanBrokerDelete - unrelated confidential holder";
1774 using namespace jtx;
1777 auto const withFix = features[fixCleanup3_5_0];
1779 Account const issuer(
"issuer");
1785 for (
bool const carolConverts : {
false,
true})
1787 Env env(*
this, features);
1788 env.
fund(
XRP(100'000), issuer, alice, carol);
1792 mptt.
create({.flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance});
1798 env(
pay(issuer, alice, mpt(100'000)));
1799 env(
pay(issuer, carol, mpt(100)));
1805 mptt.
set({.account = issuer, .issuerPubKey = mptt.
getPubKey(issuer)});
1807 Vault const vault{env};
1808 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
1812 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(10'000)}));
1815 auto const brokerKeylet =
1817 env(
set(alice, vaultKeylet.key));
1820 env(coverDeposit(alice, brokerKeylet.key, mpt(5'000).value()));
1823 auto const broker = env.
le(brokerKeylet);
1824 if (!BEAST_EXPECT(broker))
1826 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
1828 auto const brokerPseudoID = broker->at(sfAccount);
1830 if (!BEAST_EXPECT(env.
le(pseudoMptKey)))
1836 Account const brokerHolder(
"broker", brokerPseudoID);
1848 mptt.
convert({.account = carol, .amt = 1, .holderPubKey = mptt.
getPubKey(carol)});
1853 if (carolConverts && !withFix)
1859 BEAST_EXPECT(env.
le(brokerKeylet) !=
nullptr);
1860 BEAST_EXPECT(env.
le(pseudoMptKey) !=
nullptr);
1865 env(coverWithdraw(alice, brokerKeylet.key, mpt(5'000).value()));
1872 env(del(alice, brokerKeylet.key));
1874 BEAST_EXPECT(env.
le(brokerKeylet) ==
nullptr);
1875 BEAST_EXPECT(env.
le(pseudoMptKey) ==
nullptr);
1882 testcase <<
"LoanBrokerDelete - frozen broker pseudo-account IOU";
1883 using namespace jtx;
1886 Account const issuer(
"issuer");
1889 auto const withFix = features[fixCleanup3_2_0];
1892 env.
fund(
XRP(100'000), issuer, alice);
1895 auto const iou = issuer[
"IOU"];
1898 env(
trust(alice, iou(1'000'000)));
1900 env(
pay(issuer, alice, iou(100'000)));
1904 Vault const vault{env};
1905 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = iou.asset()});
1910 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = iou(10'000)}));
1914 auto const brokerKeylet =
1916 env(
set(alice, vaultKeylet.key));
1920 env(coverDeposit(alice, brokerKeylet.key, iou(5'000)));
1924 auto const broker = env.
le(brokerKeylet);
1925 if (!BEAST_EXPECT(broker))
1927 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
1930 auto const brokerPseudoID = broker->at(sfAccount);
1931 auto const brokerPseudo =
Account(
"BrokerPseudo", brokerPseudoID);
1934 env(
trust(issuer, brokerPseudo[
"IOU"](0), tfSetFreeze));
1938 auto const aliceBalanceBefore = env.
balance(alice, iou);
1958 BEAST_EXPECT(env.
le(brokerKeylet) !=
nullptr);
1961 auto const aliceBalanceAfter = env.
balance(alice, iou);
1962 BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore);
1968 testcase <<
"LoanBrokerDelete - auth-required broker pseudo-account MPT "
1969 << (features[fixCleanup3_4_0] ?
"post-fix" :
"pre-fix");
1970 using namespace jtx;
1973 Account const issuer(
"issuer");
1976 Env env(*
this, features);
1977 env.
fund(
XRP(100'000), issuer, alice);
1990 .flags = tfMPTRequireAuth | tfMPTCanTransfer,
1991 .authHolder =
true});
1996 Vault const vault{env};
1997 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
2002 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(10'000)}));
2006 auto const brokerKeylet =
2008 env(
set(alice, vaultKeylet.key));
2012 env(coverDeposit(alice, brokerKeylet.key, mpt(5'000).value()));
2016 auto const broker = env.
le(brokerKeylet);
2017 if (!BEAST_EXPECT(broker))
2019 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
2022 auto const brokerPseudoID = broker->at(sfAccount);
2027 auto const pseudoMptKey =
keylet::mptoken(tester.issuanceID(), brokerPseudoID);
2028 auto const pseudoMpt = env.
le(pseudoMptKey);
2029 if (!BEAST_EXPECT(pseudoMpt))
2031 BEAST_EXPECT(!pseudoMpt->isFlag(lsfMPTAuthorized));
2034 auto const aliceBalanceBefore = env.
balance(alice, mpt);
2047 BEAST_EXPECT(env.
le(brokerKeylet) ==
nullptr);
2048 BEAST_EXPECT(env.
le(pseudoMptKey) ==
nullptr);
2051 auto const aliceBalanceAfter = env.
balance(alice, mpt);
2052 BEAST_EXPECT(aliceBalanceAfter > aliceBalanceBefore);
2058 using namespace jtx;
2061 Account const issuer{
"issuer"};
2066 testcase(
"LoanBrokerCoverDeposit IOU freeze checks");
2068 Vault const vault{env};
2070 env.
fund(
XRP(100'000), issuer, alice);
2071 env(
trust(alice, issuer[
"IOU"](1'000'000)));
2074 env(
pay(issuer, alice, asset(100'000)));
2077 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
2079 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
2082 auto const brokerKeylet =
2084 env(
set(alice, vaultKeylet.key));
2087 auto const broker = env.
le(brokerKeylet);
2088 if (!BEAST_EXPECT(broker))
2090 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2092 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
2095 auto runTests = [&]() {
2096 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
2099 env(
fset(issuer, asfGlobalFreeze));
2100 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
2101 env(
fclear(issuer, asfGlobalFreeze));
2104 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2105 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
2106 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2109 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
2110 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
2111 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
2115 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze));
2116 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(pseudoTer));
2117 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze));
2120 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze | tfSetDeepFreeze));
2121 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
2122 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze | tfClearDeepFreeze));
2133 testcase(
"LoanBrokerCoverDeposit MPT lock checks");
2135 Vault const vault{env};
2137 env.
fund(
XRP(100'000), issuer, alice);
2141 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
2145 env(
pay(issuer, alice, mpt(100'000)));
2148 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
2150 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(50)}));
2153 auto const brokerKeylet =
2155 env(
set(alice, vaultKeylet.key));
2158 auto const broker = env.
le(brokerKeylet);
2159 if (!BEAST_EXPECT(broker))
2161 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2163 env(coverDeposit(alice, brokerKeylet.key, mpt(10)));
2168 auto runTests = [&]() {
2170 mptt.
set({.flags = tfMPTLock});
2172 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
2173 mptt.
set({.flags = tfMPTUnlock});
2177 mptt.
set({.holder = alice, .flags = tfMPTLock});
2179 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
2180 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
2184 mptt.
set({.holder = brokerPseudo, .flags = tfMPTLock});
2186 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
2187 mptt.
set({.holder = brokerPseudo, .flags = tfMPTUnlock});
2209 testcase(
"LoanBrokerCoverWithdraw IOU self-withdrawal while individually frozen");
2211 using namespace jtx;
2214 Account const issuer{
"issuer"};
2218 Vault const vault{env};
2220 env.
fund(
XRP(100'000), issuer, alice, dest);
2221 env(
trust(alice, issuer[
"IOU"](1'000'000)));
2222 env(
trust(dest, issuer[
"IOU"](1'000'000)));
2226 env(
pay(issuer, alice, asset(100'000)));
2229 auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
2231 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
2234 auto const brokerKeylet =
2236 env(
set(alice, vaultKeylet.key));
2239 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
2242 auto runTests = [&]() {
2243 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
2246 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2253 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tesSUCCESS));
2257 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2261 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2274 using namespace jtx;
2277 Account const issuer{
"issuer"};
2282 testcase(
"LoanBrokerCoverWithdraw IOU freeze checks");
2284 Vault const vault{env};
2286 env.
fund(
XRP(100'000), issuer, alice);
2287 env(
trust(alice, issuer[
"IOU"](1'000'000)));
2290 env(
pay(issuer, alice, asset(100'000)));
2293 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
2295 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
2298 auto const brokerKeylet =
2300 env(
set(alice, vaultKeylet.key));
2303 auto const broker = env.
le(brokerKeylet);
2304 if (!BEAST_EXPECT(broker))
2306 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2308 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
2313 env(
trust(dest, asset(1'000)));
2315 auto runTests = [&]() {
2316 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
2320 env(
fset(issuer, asfGlobalFreeze));
2321 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2324 env(
fclear(issuer, asfGlobalFreeze));
2327 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze));
2328 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2331 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze));
2334 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze | tfSetDeepFreeze));
2335 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2338 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze | tfClearDeepFreeze));
2341 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2342 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2346 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tesSUCCESS));
2347 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2349 alice, brokerKeylet.key, asset(
isTesSuccess(expectedTec) ? 2 : 1)));
2352 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
2353 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2357 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
2358 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
2360 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2363 env(
trust(issuer, asset(0), dest, tfSetFreeze));
2364 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2367 env(
trust(issuer, asset(0), dest, tfClearFreeze));
2368 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2371 env(
trust(issuer, asset(0), dest, tfSetFreeze | tfSetDeepFreeze));
2372 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2375 env(
trust(issuer, asset(0), dest, tfClearFreeze | tfClearDeepFreeze));
2378 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2379 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2380 kDestination(issuer),
2382 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2383 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2394 testcase(
"LoanBrokerCoverWithdraw MPT lock checks");
2396 Vault const vault{env};
2398 env.
fund(
XRP(100'000), issuer, alice);
2402 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
2406 env(
pay(issuer, alice, mpt(100'000)));
2409 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
2411 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(50)}));
2414 auto const brokerKeylet =
2416 env(
set(alice, vaultKeylet.key));
2419 auto const broker = env.
le(brokerKeylet);
2420 if (!BEAST_EXPECT(broker))
2422 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2424 env(coverDeposit(alice, brokerKeylet.key, mpt(10)));
2432 auto runTests = [&]() {
2433 auto const withFix = env.
current()->rules().enabled(fixCleanup3_3_0);
2440 mptt.
set({.flags = tfMPTLock});
2442 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2445 mptt.
set({.flags = tfMPTUnlock});
2449 mptt.
set({.holder = brokerPseudo, .flags = tfMPTLock});
2451 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2454 mptt.
set({.holder = brokerPseudo, .flags = tfMPTUnlock});
2458 mptt.
set({.holder = alice, .flags = tfMPTLock});
2460 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2462 Ter(submitterToDest));
2464 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
2465 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
2468 env(coverDeposit(alice, brokerKeylet.key, mpt(1)));
2472 mptt.
set({.holder = dest, .flags = tfMPTLock});
2474 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2477 mptt.
set({.holder = dest, .flags = tfMPTUnlock});
2481 mptt.
set({.holder = alice, .flags = tfMPTLock});
2483 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2484 kDestination(issuer),
2486 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
2487 env(coverDeposit(alice, brokerKeylet.key, mpt(1)));
2501 using namespace jtx;
2502 Account const issuer(
"broker");
2503 Account const broker(
"issuer");
2504 Account const dest(
"destination");
2505 auto const token = issuer[
"IOU"];
2507 enum class TrustState {
2515 auto test = [&](TrustState trustState) {
2518 testcase <<
"RIPD-4274 IOU with state: " <<
static_cast<int>(trustState);
2520 auto setTrustLine = [&](
Account const& acct, TrustState state) {
2523 case TrustState::RequireAuth:
2524 env(
trust(issuer,
token(0), acct, tfSetfAuth));
2526 case TrustState::ZeroLimit: {
2530 jv[sfQualityIn] = 10'000'000;
2534 case TrustState::ReachedLimit: {
2536 env(
pay(issuer, acct,
token(1'000)));
2540 case TrustState::NearLimit: {
2542 env(
pay(issuer, acct,
token(950)));
2546 case TrustState::NoTrustLine:
2550 BEAST_EXPECT(
false);
2555 env.
fund(
XRP(1'000), issuer, broker, dest);
2558 if (trustState == TrustState::RequireAuth)
2560 env(
fset(issuer, asfRequireAuth));
2563 setTrustLine(broker, TrustState::RequireAuth);
2566 setTrustLine(dest, trustState);
2569 env(
pay(issuer, broker,
token(2'000)));
2572 Vault const vault(env);
2573 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
2578 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2581 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
2587 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2590 auto const brokerKeylet =
2606 if (trustState == TrustState::RequireAuth)
2608 env(
fclear(issuer, asfRequireAuth));
2619 test(TrustState::RequireAuth);
2620 test(TrustState::ZeroLimit);
2621 test(TrustState::ReachedLimit);
2622 test(TrustState::NearLimit);
2623 test(TrustState::NoTrustLine);
2629 using namespace jtx;
2630 Account const issuer(
"broker");
2631 Account const broker(
"issuer");
2632 Account const dest(
"destination");
2634 enum class MPTState {
2640 auto test = [&](MPTState mptState) {
2643 testcase <<
"RIPD-4274 MPT with state: " <<
static_cast<int>(mptState);
2645 env.
fund(
XRP(1'000), issuer, broker, dest);
2651 case MPTState::RequireAuth: {
2655 .holders = {broker, dest},
2662 {.account = issuer, .holder = dest, .flags = tfMPTUnauthorize});
2665 case MPTState::ReachedMAX: {
2669 .holders = {broker, dest},
2673 BEAST_EXPECT(env.
balance(issuer, tester) == tester(-4'000));
2676 case MPTState::NoMPT: {
2680 .holders = {broker},
2686 return std::nullopt;
2689 if (!BEAST_EXPECT(maybeToken))
2692 auto const&
token = *maybeToken;
2694 Vault const vault(env);
2695 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
2700 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2703 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
2709 BEAST_EXPECT(env.
ter() == err);
2715 {.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2719 auto const brokerKeylet =
2731 BEAST_EXPECT(env.
ter() == err);
2735 test(MPTState::RequireAuth);
2736 test(MPTState::ReachedMAX);
2737 test(MPTState::NoMPT);
2751 std::string{
"CoverWithdraw with credential-based deposit preauth "} +
2752 (features[fixCleanup3_4_0] ?
"post-fix" :
"pre-fix"));
2753 using namespace jtx;
2754 using namespace std::chrono_literals;
2756 bool const fix340Enabled = features[fixCleanup3_4_0];
2758 Env env(*
this, features);
2760 Account const broker{
"broker"};
2762 Account const credIssuer{
"credIssuer"};
2763 char const credType[] =
"abcde";
2765 env.
fund(
XRP(10'000), broker, dest, credIssuer);
2766 env(
fset(dest, asfDepositAuth));
2771 Vault const vault(env);
2772 auto const [vaultTx, vaultKeylet] = vault.create({.owner = broker, .asset = asset});
2776 env(vault.deposit({.depositor = broker, .id = vaultKeylet.key, .amount = asset(1'000)}));
2779 auto const brokerKeylet =
2787 auto coverWithdrawToDest = [&]() {
2798 env.
current()->header().parentCloseTime.time_since_epoch().count() + 100;
2799 jv[sfExpiration.jsonName] = expiration;
2805 auto const credIdx =
2820 env(coverWithdrawToDest(),
2834 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E01E034";
2835 env(coverWithdrawToDest(),
2842 env(coverWithdrawToDest(),
2849 char const credType2[] =
"fghij";
2853 auto const credIdx2 =
2856 env(coverWithdrawToDest(),
2864 BEAST_EXPECT(env.
le(credKeylet));
2865 env(coverWithdrawToDest(),
2870 BEAST_EXPECT(!env.
le(credKeylet));
2880 using namespace jtx;
2883 Account const issuer{
"issuer"};
2893 Vault const vault{env};
2895 env.
fund(
XRP(100'000), issuer, alice);
2897 env(
fset(issuer, asfAllowTrustLineClawback));
2901 env(
trust(alice, iou(1'000'000)));
2903 env(
pay(issuer, alice, iou(1'000)));
2906 auto [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = iou});
2910 auto const brokerKeylet =
2912 env(
set(alice, vaultKeylet.key));
2915 env(coverDeposit(alice, brokerKeylet.key, iou(10)));
2918 return {brokerKeylet, iou};
2922 TER const expected =
2926 testcase(
"Cover precision guard: Deposit zero-at-scale");
2927 Env env{*
this, features};
2928 auto const [brokerKeylet, iou] = setup(env);
2930 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2931 env(coverDeposit(alice, brokerKeylet.key, subUlpAmt),
Ter(expected));
2935 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
2936 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
2941 testcase(
"Cover precision guard: Deposit rounds down");
2949 Env env{*
this, features};
2950 auto const [brokerKeylet, iou] = setup(env);
2952 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2953 env(coverDeposit(alice, brokerKeylet.key, subUlpAmt),
Ter(
tesSUCCESS));
2955 auto const brokerAfter = env.
le(brokerKeylet);
2956 if (!BEAST_EXPECT(brokerAfter))
2960 BEAST_EXPECT(brokerAfter->at(sfCoverAvailable) - coverBefore == delta);
2969 if (features[fixCleanup3_2_0])
2971 testcase(
"Cover precision guard: Deposit rounding bound");
2972 Env env{*
this, features};
2973 auto const [brokerKeylet, iou] = setup(env);
2974 Number const oneUlp{1, -14};
2981 auto const broker = env.
le(brokerKeylet);
2982 if (!BEAST_EXPECT(broker))
2984 Number const coverBefore = broker->at(sfCoverAvailable);
2985 env(coverDeposit(alice, brokerKeylet.key, iou(requested)),
Ter(
tesSUCCESS));
2987 auto const brokerAfter = env.
le(brokerKeylet);
2988 if (!BEAST_EXPECT(brokerAfter))
2990 Number const coverAfter = brokerAfter->at(sfCoverAvailable);
2991 Number const actual = coverAfter - coverBefore;
2992 Number const lost = requested - actual;
2993 BEAST_EXPECT(lost >=
Number{0});
2994 BEAST_EXPECT(lost < oneUlp);
2999 testcase(
"Cover precision guard: Withdraw");
3000 Env env{*
this, features};
3001 auto const [brokerKeylet, iou] = setup(env);
3003 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
3004 auto const aliceBalanceBefore = env.
balance(alice, iou);
3005 env(coverWithdraw(alice, brokerKeylet.key, subUlpAmt),
Ter(expected));
3009 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
3010 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
3011 BEAST_EXPECT(env.
balance(alice, iou) == aliceBalanceBefore);
3016 testcase(
"Cover precision guard: Clawback");
3017 Env env{*
this, features};
3018 auto const [brokerKeylet, iou] = setup(env);
3020 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
3021 env(coverClawback(issuer),
3022 kLoanBrokerId(brokerKeylet.key),
3028 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
3029 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
3036 testcase(
"Cover precision guard: MPT min amount passes");
3039 env.
fund(
XRP(100'000), issuer, alice);
3043 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
3050 env(
pay(issuer, alice, mptAsset(100)));
3053 Vault const vault{env};
3054 auto [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = mptAsset});
3058 auto const brokerKeylet =
3060 env(
set(alice, vaultKeylet.key));
3063 env(coverDeposit(alice, brokerKeylet.key, mptAsset(10)));
3066 env(coverDeposit(alice, brokerKeylet.key, mptAsset(1)),
Ter(
tesSUCCESS));
3069 env(coverWithdraw(alice, brokerKeylet.key, mptAsset(1)),
Ter(
tesSUCCESS));
3072 env(coverClawback(issuer),
3073 kLoanBrokerId(brokerKeylet.key),
3081 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
std::string asString() const
Returns the unquoted string value.
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 & ConstRef
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 testLoanBrokerDeleteRequireAuthMPT(FeatureBitset features)
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 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::ConstRef)> checkBroker, std::function< void(SLE::ConstRef)> changeBroker, std::function< void(SLE::ConstRef)> checkChangedBroker)
void testAmB06VaultFreezeCheckMissing()
void testLoanBrokerCoverDepositNullVault()
void testLoanBrokerDeleteConfidentialCOA(FeatureBitset features)
void testCoverWithdrawCredentialDepositPreauth(FeatureBitset features)
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)
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.
void disableFeature(UInt256 const feature)
void enableFeature(UInt256 const feature)
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.
static constexpr auto holderEncryptedInbox
std::optional< Buffer > getEncryptedBalance(Account const &account, EncryptedBalanceType option=holderEncryptedInbox) const
std::uint32_t generateKeyPair(Account const &account)
static constexpr auto issuerEncryptedBalance
MPTID const & issuanceID() const
static constexpr auto holderEncryptedSpending
std::optional< Buffer > getPubKey(Account const &account, std::optional< std::uint32_t > epoch=std::nullopt) const
static constexpr auto auditorEncryptedBalance
void create(MPTCreate const &arg=MPTCreate{}, std::source_location const &loc=std::source_location::current())
void set(MPTSet const &set={}, std::source_location const &loc=std::source_location::current())
std::int64_t getIssuanceConfidentialBalance() const
void authorize(MPTAuthorize const &arg=MPTAuthorize{}, std::source_location const &loc=std::source_location::current())
void convert(MPTConvert const &arg=MPTConvert{}, std::source_location const &loc=std::source_location::current())
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 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.
Keylet loan(UInt256 const &loanBrokerID, SeqProxy const &loanSeq) noexcept
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.
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Keylet keylet(test::jtx::Account const &subject, test::jtx::Account const &issuer, std::string_view credType)
Deposit preauthorize operations.
json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
json::Value set(AccountID const &account, UInt256 const &vaultId, uint32_t flags)
json::Value coverWithdraw(AccountID const &account, UInt256 const &brokerID, STAmount const &amount, uint32_t flags)
json::Value coverDeposit(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.