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/protocol/AccountID.h>
33#include <xrpl/protocol/Feature.h>
34#include <xrpl/protocol/Indexes.h>
35#include <xrpl/protocol/Issue.h>
36#include <xrpl/protocol/LedgerFormats.h>
37#include <xrpl/protocol/MPTIssue.h>
38#include <xrpl/protocol/Protocol.h>
39#include <xrpl/protocol/SField.h>
40#include <xrpl/protocol/STAmount.h>
41#include <xrpl/protocol/STObject.h>
42#include <xrpl/protocol/STTx.h>
43#include <xrpl/protocol/Serializer.h>
44#include <xrpl/protocol/TER.h>
45#include <xrpl/protocol/TxFlags.h>
46#include <xrpl/protocol/TxFormats.h>
47#include <xrpl/protocol/Units.h>
48#include <xrpl/protocol/jss.h>
49#include <xrpl/tx/ApplyContext.h>
50#include <xrpl/tx/Transactor.h>
51#include <xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h>
80 auto failAll = [
this](
FeatureBitset features,
bool goodVault =
false) {
81 Env env(*
this, features);
88 Vault const vault{env};
89 auto const [tx,
keylet] = vault.create({.owner = alice, .asset = asset});
92 BEAST_EXPECT(
static_cast<bool>(env.
le(
keylet)) == goodVault);
100 env(coverDeposit(alice, brokerKeylet.key, asset(1000)),
Ter(
temDISABLED));
102 env(coverWithdraw(alice, brokerKeylet.key, asset(1000)),
Ter(
temDISABLED));
105 env(coverClawback(alice), kLoanBrokerId(brokerKeylet.key),
Ter(
temDISABLED));
107 env(coverClawback(alice),
108 kLoanBrokerId(brokerKeylet.key),
114 failAll(
all_ - featureMPTokensV1);
115 failAll(
all_ - featureSingleAssetVault - featureLendingProtocol);
116 failAll(
all_ - featureSingleAssetVault);
117 failAll(
all_ - featureLendingProtocol,
true);
147 auto const& asset = vault.asset.raw();
153 else if (asset.holds<
Issue>())
163 assetLabel =
"Unknown ";
165 testcase <<
"Lifecycle: " << assetLabel << label;
174 badMptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
176 return badMptt[
"BAD"];
178 static PrettyAsset const kBadIouAsset = evan[
"BAD"];
179 static Account const kNonExistent{
"NonExistent"};
180 static PrettyAsset const kGhostIouAsset = kNonExistent[
"GST"];
181 PrettyAsset const vaultPseudoIouAsset = vault.pseudoAccount[
"PSD"];
186 auto const badBrokerPseudo = [&]() {
187 if (
auto const le = env.
le(badKeylet); BEAST_EXPECT(le))
189 return Account{
"Bad Broker pseudo-account", le->at(sfAccount)};
192 return vault.pseudoAccount;
194 PrettyAsset const badBrokerPseudoIouAsset = badBrokerPseudo[
"WAT"];
199 auto jtx = env.
jt(
set(alice, vault.vaultID));
208 if (
auto broker = env.
le(
keylet); BEAST_EXPECT(broker))
212 BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
213 BEAST_EXPECT(broker->at(sfAccount) != alice.
id());
214 BEAST_EXPECT(broker->at(sfOwner) == alice.
id());
215 BEAST_EXPECT(broker->at(sfFlags) == 0);
216 BEAST_EXPECT(broker->at(sfSequence) == env.
seq(alice) - 1);
217 BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
218 BEAST_EXPECT(broker->at(sfLoanSequence) == 1);
219 BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
220 BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
230 Account const pseudoAccount{
"Broker pseudo-account", broker->at(sfAccount)};
233 if (
auto const pseudo = env.
le(pseudoKeylet); BEAST_EXPECT(pseudo))
239 pseudo->at(sfFlags) == (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth));
240 BEAST_EXPECT(pseudo->at(sfSequence) == 0);
241 BEAST_EXPECT(pseudo->at(sfBalance) == beast::kZero);
242 BEAST_EXPECT(pseudo->at(sfOwnerCount) == (vault.asset.raw().native() ? 0 : 1));
243 BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
244 BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
245 BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
246 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
247 BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
248 BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
249 BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
250 BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
251 BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
252 BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
253 BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
254 BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
255 BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
256 BEAST_EXPECT(!pseudo->isFieldPresent(sfFirstNFTokenSequence));
257 BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
258 BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
259 BEAST_EXPECT(pseudo->at(sfLoanBrokerID) ==
keylet.key);
265 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
266 if (BEAST_EXPECT(accountInfo.isObject()))
268 auto const& accountData = accountInfo[jss::result][jss::account_data];
269 if (BEAST_EXPECT(accountData.isObject()))
271 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
274 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
275 if (BEAST_EXPECT(pseudoInfo.isObject()))
277 BEAST_EXPECT(pseudoInfo[jss::type] ==
"LoanBroker");
282 auto verifyCoverAmount =
283 [&env, &vault, &pseudoAccount, &broker, &
keylet,
this](
auto n) {
287 if (BEAST_EXPECT(broker))
289 auto const amount = vault.asset(n);
290 BEAST_EXPECT(broker->at(sfCoverAvailable) == amount.number());
300 env(coverDeposit(alice, vault.vaultID, vault.asset(10)),
Ter(
tecNO_ENTRY));
302 verifyCoverAmount(0);
312 env(coverClawback(evan), kLoanBrokerId(vault.vaultID),
Ter(
tecNO_ENTRY));
315 if (vault.asset.raw().native())
322 if (vault.asset.raw().holds<
Issue>())
329 env(coverClawback(alice),
330 kAmount(vaultPseudoIouAsset(1)),
334 env(coverClawback(issuer),
335 kLoanBrokerId(
keylet.key),
336 kAmount(badBrokerPseudoIouAsset(10)),
338 PrettyAsset const brokerWrongCurrencyAsset = pseudoAccount[
"WAT"];
339 env(coverClawback(issuer),
340 kLoanBrokerId(
keylet.key),
341 kAmount(brokerWrongCurrencyAsset(10)),
348 BEAST_EXPECT(vault.asset.raw().holds<
MPTIssue>());
353 env(coverClawback(issuer),
354 kLoanBrokerId(
keylet.key),
361 env(coverDeposit(alice,
keylet.key, vault.asset(10)));
363 verifyCoverAmount(10);
370 env(coverWithdraw(alice, vault.vaultID, vault.asset(10)),
Ter(
tecNO_ENTRY));
374 if (!vault.asset.raw().native())
377 env(coverWithdraw(alice,
keylet.key, vault.asset(1)),
378 kDestination(bystander),
383 env(coverWithdraw(alice,
keylet.key, vault.asset(1)),
388 env(coverWithdraw(alice,
keylet.key, vault.asset(7)));
390 verifyCoverAmount(3);
393 env(coverDeposit(alice,
keylet.key, vault.asset(5)));
395 verifyCoverAmount(8);
399 env(coverWithdraw(alice,
keylet.key, vault.asset(1)), kDestination(evan));
401 verifyCoverAmount(7);
405 env(coverWithdraw(alice,
keylet.key, vault.asset(1)), kDestination(evan),
Dtag(3));
407 verifyCoverAmount(6);
409 if (!vault.asset.raw().native())
412 env(coverClawback(issuer), kLoanBrokerId(
keylet.key),
kAmount(vault.asset(2)));
414 verifyCoverAmount(4);
417 env(coverDeposit(alice,
keylet.key, vault.asset(5)));
419 verifyCoverAmount(9);
422 for (
auto const& tx : {
425 coverClawback(issuer),
426 kLoanBrokerId(
keylet.key),
431 coverClawback(issuer),
432 kLoanBrokerId(
keylet.key),
438 coverClawback(issuer),
439 kLoanBrokerId(
keylet.key),
446 coverClawback(issuer),
447 kLoanBrokerId(
keylet.key),
457 verifyCoverAmount(0);
460 env(coverDeposit(alice,
keylet.key, vault.asset(6)));
462 verifyCoverAmount(6);
467 env(
set(alice, vault.vaultID), kLoanBrokerId(
keylet.key));
472 changeBroker(broker);
478 if (BEAST_EXPECT(broker) && checkChangedBroker)
479 checkChangedBroker(broker);
484 env(
set(alice, vault.vaultID),
485 kLoanBrokerId(broker->key()),
492 if (BEAST_EXPECT(broker))
494 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
495 BEAST_EXPECT(!broker->isFieldPresent(sfData));
505 env(del(alice, badKeylet.key));
510 auto const aliceBalance = env.balance(alice, vault.asset);
511 auto const coverFunds = env.balance(pseudoAccount, vault.asset);
512 BEAST_EXPECT(coverFunds.number() == broker->at(sfCoverAvailable));
513 BEAST_EXPECT(coverFunds != beast::kZero);
514 verifyCoverAmount(6);
527 env(del(alice,
keylet.key));
531 BEAST_EXPECT(!broker);
532 auto pseudo = env.le(pseudoKeylet);
533 BEAST_EXPECT(!pseudo);
535 auto const expectedBalance = aliceBalance + coverFunds -
536 (aliceBalance.value().native() ?
STAmount(env.current()->fees().base.value())
538 env.require(
Balance(alice, expectedBalance));
539 env.require(
Balance(pseudoAccount, vault.asset(
kNone)));
553 Account const issuer{
"issuer"};
560 Account const bystander{
"bystander"};
568 env(
fset(issuer, asfAllowTrustLineClawback));
574 env(
trust(alice, iouAsset(1'000'000)));
575 env(
trust(evan, iouAsset(1'000'000)));
577 env(
pay(issuer, evan, iouAsset(100'000)));
578 env(
pay(issuer, alice, iouAsset(100'000)));
582 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
588 env(
pay(issuer, alice, mptAsset(100'000)));
589 env(
pay(issuer, evan, mptAsset(100'000)));
592 std::array const assets{xrpAsset, iouAsset, mptAsset};
596 for (
auto const& asset : assets)
598 auto [tx,
keylet] = vault.create({.owner = alice, .asset = asset});
606 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(50)}));
610 auto [tx,
keylet] = vault.create({.owner = alice, .asset = iouAsset});
615 return {iouAsset,
keylet.key, le->at(sfAccount)};
618 return {iouAsset,
keylet.key, evan.
id()};
621 auto const aliceOriginalCount = env.
ownerCount(alice);
624 for (
auto const& vault : vaults)
629 auto const accountInfo = env.
rpc(
"account_info", pseudoStr);
630 if (BEAST_EXPECT(accountInfo.isObject()))
632 auto const& accountData = accountInfo[jss::result][jss::account_data];
633 if (BEAST_EXPECT(accountData.isObject()))
635 BEAST_EXPECT(accountData[jss::Account] == pseudoStr);
636 BEAST_EXPECT(accountData[sfVaultID] ==
to_string(vault.vaultID));
638 auto const& pseudoInfo = accountInfo[jss::result][jss::pseudo_account];
639 if (BEAST_EXPECT(pseudoInfo.isObject()))
641 BEAST_EXPECT(pseudoInfo[jss::type] ==
"Vault");
661 env(
set(evan, vault.vaultID),
665 env(
set(evan, vault.vaultID),
669 env(
set(evan, vault.vaultID),
673 env(
set(evan, vault.vaultID),
678 env(
set(evan, vault.vaultID),
683 env(
set(evan, vault.vaultID),
688 env(
set(evan, vault.vaultID),
695 env(
set(evan, vault.vaultID),
696 kCoverRateMinimum(tenthBipsZero),
702 env(
set(evan, vault.vaultID),
704 kCoverRateLiquidation(tenthBipsZero),
727 BEAST_EXPECT(!broker->isFieldPresent(sfManagementFeeRate));
728 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateMinimum));
729 BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateLiquidation));
730 BEAST_EXPECT(!broker->isFieldPresent(sfData));
731 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
732 BEAST_EXPECT(broker->at(sfDebtMaximum) == 0);
733 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 0);
734 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 0);
736 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount + 4);
750 env(
set(evan, vault.vaultID),
751 kLoanBrokerId(broker->key()),
754 env(
set(alice, vault.vaultID),
755 kLoanBrokerId(broker->key()),
759 env(
set(alice, vault.vaultID),
760 kLoanBrokerId(broker->key()),
764 env(
set(alice, vault.vaultID),
765 kLoanBrokerId(broker->key()),
770 testData =
"Test Data 1234";
772 env(
set(alice, vault.vaultID),
773 kLoanBrokerId(broker->key()),
778 env(
set(alice, vault.vaultID),
779 kLoanBrokerId(broker->key()),
780 kDebtMaximum(
Number(-175, -1)),
783 if (vault.asset.integral())
785 env(
set(alice, vault.vaultID),
786 kLoanBrokerId(broker->key()),
788 kDebtMaximum(debtMax),
793 env(
set(alice, vault.vaultID),
794 kLoanBrokerId(broker->key()),
796 kDebtMaximum(debtMax));
800 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
802 auto const actual = broker->at(sfDebtMaximum);
809 "non-default fields",
818 testData =
"spam spam spam spam";
831 BEAST_EXPECT(broker->at(sfManagementFeeRate) == 123);
832 BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 100);
833 BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 200);
834 BEAST_EXPECT(broker->at(sfDebtMaximum) ==
Number(9));
835 BEAST_EXPECT(
checkVL(broker->at(sfData), testData));
839 env(
set(alice, vault.vaultID),
840 kLoanBrokerId(broker->key()),
846 BEAST_EXPECT(!broker->isFieldPresent(sfData));
847 BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
851 BEAST_EXPECT(env.
ownerCount(alice) == aliceOriginalCount);
864 Account const issuer{
"issuer"};
867 Vault const vault{env};
869 env.
fund(
XRP(100'000), issuer, alice);
874 return getAsset(env, issuer, alice);
875 env(
trust(alice, issuer[
"IOU"](1'000'000)));
880 env(
pay(issuer, alice, asset(100'000)));
883 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
886 auto const le = env.
le(vaultKeylet);
888 if (BEAST_EXPECT(le))
889 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
895 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
902 auto broker = env.
le(brokerKeylet);
903 if (!BEAST_EXPECT(broker))
906 auto testZeroBrokerID = [&](
auto&& getTxJv) {
909 jv[sfLoanBrokerID] =
"";
917 auto testZeroVaultID = [&](
auto&& getTxJv) {
932 testZeroBrokerID([&]() {
return coverDeposit(alice, brokerKeylet.key, asset(10)); });
935 env(coverDeposit(alice, brokerKeylet.key, issuer[
"BAD"](10)),
Ter(
tecWRONG_ASSET));
938 env(
pay(alice, issuer, asset(100'000 - 50)));
940 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)),
947 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)));
954 testZeroBrokerID([&]() {
return coverWithdraw(alice, brokerKeylet.key, asset(10)); });
957 env(coverWithdraw(alice, brokerKeylet.key, issuer[
"BAD"](10)),
Ter(
tecWRONG_ASSET));
961 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
967 env.fund(
XRP(1'000), dest);
969 env(
fset(dest, asfRequireDest));
970 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
973 env(
fclear(dest, asfRequireDest));
976 env(
fset(dest, asfDepositAuth));
977 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
980 env(
fclear(dest, asfDepositAuth));
984 env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
992 testZeroBrokerID([&]() {
994 coverClawback(alice),
995 kLoanBrokerId(brokerKeylet.key),
1002 env(coverClawback(issuer),
1003 kLoanBrokerId(brokerKeylet.key),
1008 env(
fset(issuer, asfAllowTrustLineClawback | asfNoFreeze));
1010 env(coverClawback(issuer),
1011 kLoanBrokerId(brokerKeylet.key),
1018 env(coverClawback(issuer),
1019 kLoanBrokerId(brokerKeylet.key),
1028 Account const borrower{
"borrower"};
1029 env.
fund(
XRP(1'000), borrower);
1030 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
1031 Sig(sfCounterpartySignature, alice),
1035 testZeroBrokerID([&]() {
return del(alice, brokerKeylet.key); });
1041 auto const loanKeylet =
keylet::loan(brokerKeylet.key, 1);
1042 env(
loan::pay(borrower, loanKeylet.key, asset(50).value()));
1045 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
1048 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
1055 env(del(alice, brokerKeylet.key));
1061 testZeroBrokerID([&]() {
1062 return env.
json(
set(alice, vaultInfo.
vaultID), kLoanBrokerId(brokerKeylet.key));
1065 testZeroVaultID([&]() {
1066 return env.
json(
set(alice, vaultInfo.
vaultID), kLoanBrokerId(brokerKeylet.key));
1071 env(
fclear(issuer, asfDefaultRipple));
1076 env(
fset(issuer, asfDefaultRipple));
1082 env(
pay(alice, issuer, amt));
1092 testcase(
"Invalid LoanBrokerCoverClawback");
1093 using namespace jtx;
1099 Account const issuer{
"issuer"};
1100 auto const usd = alice[
"USD"];
1102 env.
fund(
XRP(100'000), alice);
1105 auto jtx = env.
jt(coverClawback(alice),
kAmount(usd(100)));
1112 jtx.jv[sfAmount] = bad.getJson();
1116 auto const jrr = env.
rpc(
"submit",
strHex(s.
slice()))[jss::result];
1118 BEAST_EXPECT(jrr[jss::error] ==
"invalidTransaction");
1119 BEAST_EXPECT(jrr[jss::error_exception] ==
"invalid native account");
1132 MPTTester const mpt({.env = env, .issuer = issuer, .holders = {alice}});
1141 testcase(
"Invalid LoanBrokerCoverDeposit");
1142 using namespace jtx;
1152 testcase(
"Invalid LoanBrokerCoverWithdraw");
1153 using namespace jtx;
1171 using namespace jtx;
1172 testcase(
"Invalid LoanBrokerDelete");
1185 using namespace jtx;
1203 using namespace jtx;
1212 Vault const vault{env};
1213 auto const [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1225 STTx tx{ttLOAN_BROKER_COVER_DEPOSIT, [](
STObject&) {}};
1239 auto const vaultID = (*sleBroker)[sfVaultID];
1255 testcase(
"Require Auth - Implicit Pseudo-account authorization");
1256 using namespace jtx;
1259 Account const issuer{
"issuer"};
1264 env.
fund(
XRP(100'000), issuer, alice);
1271 .flags =
kMptDexFlags | tfMPTRequireAuth | tfMPTCanClawback | tfMPTCanLock,
1275 env(
pay(issuer, alice, asset(100'000)));
1279 asset.authorize({.account = issuer, .holder = alice, .flags = tfMPTUnauthorize});
1280 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1284 auto const le = env.
le(vaultKeylet);
1286 if (BEAST_EXPECT(le))
1287 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
1297 .flags = tfMPTUnauthorize,
1300 auto forUnauthAuth = [&](
auto&& doTx) {
1301 for (
auto const flag : {tfMPTUnauthorize, 0u})
1303 asset.authorize({.account = issuer, .holder = alice, .flags = flag});
1313 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(51)}),
1320 env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = asset(1)}),
1326 forUnauthAuth([&](
auto) { env(
set(alice, vaultInfo.
vaultID)); });
1328 auto const broker = env.
le(brokerKeylet);
1329 if (!BEAST_EXPECT(broker))
1331 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
1336 .holder = brokerPseudo,
1337 .flags = tfMPTUnauthorize,
1343 env(coverDeposit(alice, brokerKeylet.key, vaultInfo.
asset(10)), err);
1349 env(coverWithdraw(alice, brokerKeylet.key, vaultInfo.
asset(5)), err);
1353 forUnauthAuth([&](
bool) {
1354 env(coverClawback(issuer),
1355 kLoanBrokerId(brokerKeylet.key),
1363 testcase(
"testLoanBrokerSetDebtMaximum");
1364 using namespace jtx;
1366 Account const issuer{
"issuer"};
1369 Vault const vault{env};
1371 env.
fund(
XRP(100'000), issuer, alice);
1376 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1384 env(
pay(issuer, alice, asset(100'000)));
1387 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1390 auto const le = env.
le(vaultKeylet);
1392 if (BEAST_EXPECT(le))
1393 return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
1399 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
1406 Account const borrower{
"borrower"};
1407 env.
fund(
XRP(1'000), borrower);
1408 env(
loan::set(borrower, brokerKeylet.key, asset(50).value()),
1409 Sig(sfCounterpartySignature, alice),
1411 auto const broker = env.
le(brokerKeylet);
1412 if (!BEAST_EXPECT(broker))
1415 BEAST_EXPECT(broker->at(sfDebtTotal) == 50);
1416 auto debtTotal = broker->at(sfDebtTotal);
1419 tx2[sfLoanBrokerID] =
to_string(brokerKeylet.key);
1420 tx2[sfDebtMaximum] = debtTotal - 1;
1423 tx2[sfDebtMaximum] = debtTotal + 1;
1426 tx2[sfDebtMaximum] = 0;
1433 auto const dm =
power(2, 64) - 1;
1435 tx2[sfDebtMaximum] = dm;
1440 auto const dm =
power(2, 63) - 1;
1442 tx2[sfDebtMaximum] = dm;
1447 auto const dm =
power(2, 63) - 3;
1449 tx2[sfDebtMaximum] = dm;
1454 auto const dm = 2 * (
power(2, 62) - 1) + 1;
1456 tx2[sfDebtMaximum] = dm;
1460 tx2[sfDebtMaximum] =
Number{9223372036854775807, 0};
1468 using namespace jtx;
1469 Account const issuer(
"issuer");
1470 Account const holder(
"holder");
1471 Account const& broker = issuer;
1473 auto test = [&](
auto&& getToken) {
1476 env.
fund(
XRP(1'000), issuer, holder);
1481 Vault const vault(env);
1482 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
1486 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = deposit}),
1501 auto const token = issuer[
"IOU"];
1526 for (
auto const& [
pay, max,
deposit, err] : mptTests)
1532 .holders = {holder},
1544 testcase <<
"RIPD-4466 - LoanBrokerSet disallows frozen vaults";
1545 using namespace jtx;
1548 Account const issuer{
"issuer"}, lender{
"lender"}, borrower{
"borrower"};
1549 env.
fund(
XRP(20'000), issuer, lender, borrower);
1550 auto const iou = issuer[
"IOU"];
1552 Vault const vault{env};
1553 auto [tx, vaultKeylet] = vault.create({.owner = lender, .asset = iou.asset()});
1558 auto const vaultSle = env.
le(vaultKeylet);
1559 auto const vaultPseudo = vaultSle->at(sfAccount);
1560 auto const vaultPseudoAcct =
Account(
"VaultPseudo", vaultPseudo);
1561 env(
trust(issuer, vaultPseudoAcct[
"IOU"](0), tfSetFreeze));
1569 testcase <<
"LoanBrokerDelete - locked broker pseudo-account MPT";
1570 using namespace jtx;
1573 Account const issuer(
"issuer");
1576 auto const withFix = features[fixCleanup3_2_0];
1577 Env env(*
this, features);
1578 env.
fund(
XRP(100'000), issuer, alice);
1583 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1590 env(
pay(issuer, alice, mpt(100'000)));
1594 Vault const vault{env};
1595 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
1600 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(10'000)}));
1605 env(
set(alice, vaultKeylet.key));
1609 env(coverDeposit(alice, brokerKeylet.key, mpt(5'000).value()));
1613 auto const broker = env.
le(brokerKeylet);
1614 if (!BEAST_EXPECT(broker))
1616 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
1619 auto const brokerPseudoID = broker->at(sfAccount);
1623 auto const pseudoMpt = env.
le(pseudoMptKey);
1624 if (!BEAST_EXPECT(pseudoMpt))
1630 jv[jss::Account] = issuer.
human();
1632 jv[jss::Holder] =
toBase58(brokerPseudoID);
1633 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
1634 jv[jss::Flags] = tfMPTLock;
1641 auto const sle = env.
le(pseudoMptKey);
1642 if (!BEAST_EXPECT(sle))
1644 BEAST_EXPECT(sle->isFlag(lsfMPTLocked));
1648 auto const aliceBalanceBefore = env.
balance(alice, mpt);
1660 BEAST_EXPECT(env.
le(brokerKeylet) !=
nullptr);
1663 auto const aliceBalanceAfter = env.
balance(alice, mpt);
1664 BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore);
1667 BEAST_EXPECT(env.
le(pseudoMptKey) !=
nullptr);
1675 BEAST_EXPECT(env.
le(brokerKeylet) ==
nullptr);
1678 auto const aliceBalanceAfter = env.
balance(alice, mpt);
1679 BEAST_EXPECT(aliceBalanceAfter > aliceBalanceBefore);
1682 BEAST_EXPECT(env.
le(pseudoMptKey) ==
nullptr);
1689 testcase <<
"LoanBrokerDelete - frozen broker pseudo-account IOU";
1690 using namespace jtx;
1693 Account const issuer(
"issuer");
1696 auto const withFix = features[fixCleanup3_2_0];
1699 env.
fund(
XRP(100'000), issuer, alice);
1702 auto const iou = issuer[
"IOU"];
1705 env(
trust(alice, iou(1'000'000)));
1707 env(
pay(issuer, alice, iou(100'000)));
1711 Vault const vault{env};
1712 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = iou.asset()});
1717 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = iou(10'000)}));
1722 env(
set(alice, vaultKeylet.key));
1726 env(coverDeposit(alice, brokerKeylet.key, iou(5'000)));
1730 auto const broker = env.
le(brokerKeylet);
1731 if (!BEAST_EXPECT(broker))
1733 BEAST_EXPECT(broker->at(sfCoverAvailable) > 0);
1736 auto const brokerPseudoID = broker->at(sfAccount);
1737 auto const brokerPseudo =
Account(
"BrokerPseudo", brokerPseudoID);
1740 env(
trust(issuer, brokerPseudo[
"IOU"](0), tfSetFreeze));
1744 auto const aliceBalanceBefore = env.
balance(alice, iou);
1764 BEAST_EXPECT(env.
le(brokerKeylet) !=
nullptr);
1767 auto const aliceBalanceAfter = env.
balance(alice, iou);
1768 BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore);
1774 using namespace jtx;
1777 Account const issuer{
"issuer"};
1782 testcase(
"LoanBrokerCoverDeposit IOU freeze checks");
1784 Vault const vault{env};
1786 env.
fund(
XRP(100'000), issuer, alice);
1787 env(
trust(alice, issuer[
"IOU"](1'000'000)));
1790 env(
pay(issuer, alice, asset(100'000)));
1793 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1795 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
1799 env(
set(alice, vaultKeylet.key));
1802 auto const broker = env.
le(brokerKeylet);
1803 if (!BEAST_EXPECT(broker))
1805 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
1807 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
1810 auto runTests = [&]() {
1811 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
1814 env(
fset(issuer, asfGlobalFreeze));
1815 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1816 env(
fclear(issuer, asfGlobalFreeze));
1819 env(
trust(issuer, asset(0), alice, tfSetFreeze));
1820 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1821 env(
trust(issuer, asset(0), alice, tfClearFreeze));
1824 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
1825 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1826 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
1830 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze));
1831 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(pseudoTer));
1832 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze));
1835 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze | tfSetDeepFreeze));
1836 env(coverDeposit(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
1837 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze | tfClearDeepFreeze));
1848 testcase(
"LoanBrokerCoverDeposit MPT lock checks");
1850 Vault const vault{env};
1852 env.
fund(
XRP(100'000), issuer, alice);
1856 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
1860 env(
pay(issuer, alice, mpt(100'000)));
1863 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
1865 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(50)}));
1869 env(
set(alice, vaultKeylet.key));
1872 auto const broker = env.
le(brokerKeylet);
1873 if (!BEAST_EXPECT(broker))
1875 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
1877 env(coverDeposit(alice, brokerKeylet.key, mpt(10)));
1882 auto runTests = [&]() {
1884 mptt.
set({.flags = tfMPTLock});
1886 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
1887 mptt.
set({.flags = tfMPTUnlock});
1891 mptt.
set({.holder = alice, .flags = tfMPTLock});
1893 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
1894 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
1898 mptt.
set({.holder = brokerPseudo, .flags = tfMPTLock});
1900 env(coverDeposit(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
1901 mptt.
set({.holder = brokerPseudo, .flags = tfMPTUnlock});
1923 testcase(
"LoanBrokerCoverWithdraw IOU self-withdrawal while individually frozen");
1925 using namespace jtx;
1928 Account const issuer{
"issuer"};
1932 Vault const vault{env};
1934 env.
fund(
XRP(100'000), issuer, alice, dest);
1935 env(
trust(alice, issuer[
"IOU"](1'000'000)));
1936 env(
trust(dest, issuer[
"IOU"](1'000'000)));
1940 env(
pay(issuer, alice, asset(100'000)));
1943 auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
1945 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
1949 env(
set(alice, vaultKeylet.key));
1952 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
1955 auto runTests = [&]() {
1956 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
1959 env(
trust(issuer, asset(0), alice, tfSetFreeze));
1966 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tesSUCCESS));
1970 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
1974 env(
trust(issuer, asset(0), alice, tfClearFreeze));
1987 using namespace jtx;
1990 Account const issuer{
"issuer"};
1995 testcase(
"LoanBrokerCoverWithdraw IOU freeze checks");
1997 Vault const vault{env};
1999 env.
fund(
XRP(100'000), issuer, alice);
2000 env(
trust(alice, issuer[
"IOU"](1'000'000)));
2003 env(
pay(issuer, alice, asset(100'000)));
2006 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
2008 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
2012 env(
set(alice, vaultKeylet.key));
2015 auto const broker = env.
le(brokerKeylet);
2016 if (!BEAST_EXPECT(broker))
2018 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2020 env(coverDeposit(alice, brokerKeylet.key, asset(10)));
2025 env(
trust(dest, asset(1'000)));
2027 auto runTests = [&]() {
2028 auto const fix330Enabled = env.
current()->rules().enabled(fixCleanup3_3_0);
2032 env(
fset(issuer, asfGlobalFreeze));
2033 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2036 env(
fclear(issuer, asfGlobalFreeze));
2039 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze));
2040 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2043 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze));
2046 env(
trust(issuer, asset(0), brokerPseudo, tfSetFreeze | tfSetDeepFreeze));
2047 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2050 env(
trust(issuer, asset(0), brokerPseudo, tfClearFreeze | tfClearDeepFreeze));
2053 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2054 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2058 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tesSUCCESS));
2059 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2061 alice, brokerKeylet.key, asset(
isTesSuccess(expectedTec) ? 2 : 1)));
2064 env(
trust(issuer, asset(0), alice, tfSetFreeze | tfSetDeepFreeze));
2065 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2069 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
Ter(
tecFROZEN));
2070 env(
trust(issuer, asset(0), alice, tfClearFreeze | tfClearDeepFreeze));
2072 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2075 env(
trust(issuer, asset(0), dest, tfSetFreeze));
2076 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2079 env(
trust(issuer, asset(0), dest, tfClearFreeze));
2080 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2083 env(
trust(issuer, asset(0), dest, tfSetFreeze | tfSetDeepFreeze));
2084 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2087 env(
trust(issuer, asset(0), dest, tfClearFreeze | tfClearDeepFreeze));
2090 env(
trust(issuer, asset(0), alice, tfSetFreeze));
2091 env(coverWithdraw(alice, brokerKeylet.key, asset(1)),
2092 kDestination(issuer),
2094 env(
trust(issuer, asset(0), alice, tfClearFreeze));
2095 env(coverDeposit(alice, brokerKeylet.key, asset(1)));
2106 testcase(
"LoanBrokerCoverWithdraw MPT lock checks");
2108 Vault const vault{env};
2110 env.
fund(
XRP(100'000), issuer, alice);
2114 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
2118 env(
pay(issuer, alice, mpt(100'000)));
2121 auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt});
2123 env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(50)}));
2127 env(
set(alice, vaultKeylet.key));
2130 auto const broker = env.
le(brokerKeylet);
2131 if (!BEAST_EXPECT(broker))
2133 Account const brokerPseudo(
"pseudo", broker->at(sfAccount));
2135 env(coverDeposit(alice, brokerKeylet.key, mpt(10)));
2143 auto runTests = [&]() {
2144 auto const withFix = env.
current()->rules().enabled(fixCleanup3_3_0);
2151 mptt.
set({.flags = tfMPTLock});
2153 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2156 mptt.
set({.flags = tfMPTUnlock});
2160 mptt.
set({.holder = brokerPseudo, .flags = tfMPTLock});
2162 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2165 mptt.
set({.holder = brokerPseudo, .flags = tfMPTUnlock});
2169 mptt.
set({.holder = alice, .flags = tfMPTLock});
2171 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2173 Ter(submitterToDest));
2175 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
Ter(
tecLOCKED));
2176 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
2179 env(coverDeposit(alice, brokerKeylet.key, mpt(1)));
2183 mptt.
set({.holder = dest, .flags = tfMPTLock});
2185 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2188 mptt.
set({.holder = dest, .flags = tfMPTUnlock});
2192 mptt.
set({.holder = alice, .flags = tfMPTLock});
2194 env(coverWithdraw(alice, brokerKeylet.key, mpt(1)),
2195 kDestination(issuer),
2197 mptt.
set({.holder = alice, .flags = tfMPTUnlock});
2198 env(coverDeposit(alice, brokerKeylet.key, mpt(1)));
2212 using namespace jtx;
2213 Account const issuer(
"broker");
2214 Account const broker(
"issuer");
2215 Account const dest(
"destination");
2216 auto const token = issuer[
"IOU"];
2218 enum class TrustState {
2226 auto test = [&](TrustState trustState) {
2229 testcase <<
"RIPD-4274 IOU with state: " <<
static_cast<int>(trustState);
2231 auto setTrustLine = [&](
Account const& acct, TrustState state) {
2234 case TrustState::RequireAuth:
2235 env(
trust(issuer,
token(0), acct, tfSetfAuth));
2237 case TrustState::ZeroLimit: {
2241 jv[sfQualityIn] = 10'000'000;
2245 case TrustState::ReachedLimit: {
2247 env(
pay(issuer, acct,
token(1'000)));
2251 case TrustState::NearLimit: {
2253 env(
pay(issuer, acct,
token(950)));
2257 case TrustState::NoTrustLine:
2261 BEAST_EXPECT(
false);
2266 env.
fund(
XRP(1'000), issuer, broker, dest);
2269 if (trustState == TrustState::RequireAuth)
2271 env(
fset(issuer, asfRequireAuth));
2274 setTrustLine(broker, TrustState::RequireAuth);
2277 setTrustLine(dest, trustState);
2280 env(
pay(issuer, broker,
token(2'000)));
2283 Vault const vault(env);
2284 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
2289 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2292 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
2298 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2316 if (trustState == TrustState::RequireAuth)
2318 env(
fclear(issuer, asfRequireAuth));
2329 test(TrustState::RequireAuth);
2330 test(TrustState::ZeroLimit);
2331 test(TrustState::ReachedLimit);
2332 test(TrustState::NearLimit);
2333 test(TrustState::NoTrustLine);
2339 using namespace jtx;
2340 Account const issuer(
"broker");
2341 Account const broker(
"issuer");
2342 Account const dest(
"destination");
2344 enum class MPTState {
2350 auto test = [&](MPTState mptState) {
2353 testcase <<
"RIPD-4274 MPT with state: " <<
static_cast<int>(mptState);
2355 env.
fund(
XRP(1'000), issuer, broker, dest);
2361 case MPTState::RequireAuth: {
2365 .holders = {broker, dest},
2372 {.account = issuer, .holder = dest, .flags = tfMPTUnauthorize});
2375 case MPTState::ReachedMAX: {
2379 .holders = {broker, dest},
2383 BEAST_EXPECT(env.
balance(issuer, tester) == tester(-4'000));
2386 case MPTState::NoMPT: {
2390 .holders = {broker},
2396 return std::nullopt;
2399 if (!BEAST_EXPECT(maybeToken))
2402 auto const&
token = *maybeToken;
2404 Vault const vault(env);
2405 auto const [tx,
keylet] = vault.create({.owner = broker, .asset =
token.asset()});
2410 env(vault.deposit({.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2413 env(vault.withdraw({.depositor = broker, .id = keylet.key, .amount = token(1'000)}),
2419 BEAST_EXPECT(env.
ter() == err);
2425 {.depositor = broker, .id = keylet.key, .amount = token(1'000)}));
2440 BEAST_EXPECT(env.
ter() == err);
2444 test(MPTState::RequireAuth);
2445 test(MPTState::ReachedMAX);
2446 test(MPTState::NoMPT);
2463 using namespace jtx;
2466 Account const issuer{
"issuer"};
2476 Vault const vault{env};
2478 env.
fund(
XRP(100'000), issuer, alice);
2480 env(
fset(issuer, asfAllowTrustLineClawback));
2484 env(
trust(alice, iou(1'000'000)));
2486 env(
pay(issuer, alice, iou(1'000)));
2489 auto [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = iou});
2494 env(
set(alice, vaultKeylet.key));
2497 env(coverDeposit(alice, brokerKeylet.key, iou(10)));
2500 return {brokerKeylet, iou};
2504 TER const expected =
2508 testcase(
"Cover precision guard: Deposit zero-at-scale");
2509 Env env{*
this, features};
2510 auto const [brokerKeylet, iou] = setup(env);
2512 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2513 env(coverDeposit(alice, brokerKeylet.key, subUlpAmt),
Ter(expected));
2517 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
2518 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
2523 testcase(
"Cover precision guard: Deposit rounds down");
2531 Env env{*
this, features};
2532 auto const [brokerKeylet, iou] = setup(env);
2534 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2535 env(coverDeposit(alice, brokerKeylet.key, subUlpAmt),
Ter(
tesSUCCESS));
2537 auto const brokerAfter = env.
le(brokerKeylet);
2538 if (!BEAST_EXPECT(brokerAfter))
2542 BEAST_EXPECT(brokerAfter->at(sfCoverAvailable) - coverBefore == delta);
2551 if (features[fixCleanup3_2_0])
2553 testcase(
"Cover precision guard: Deposit rounding bound");
2554 Env env{*
this, features};
2555 auto const [brokerKeylet, iou] = setup(env);
2556 Number const oneUlp{1, -14};
2563 auto const broker = env.
le(brokerKeylet);
2564 if (!BEAST_EXPECT(broker))
2566 Number const coverBefore = broker->at(sfCoverAvailable);
2567 env(coverDeposit(alice, brokerKeylet.key, iou(requested)),
Ter(
tesSUCCESS));
2569 auto const brokerAfter = env.
le(brokerKeylet);
2570 if (!BEAST_EXPECT(brokerAfter))
2572 Number const coverAfter = brokerAfter->at(sfCoverAvailable);
2573 Number const actual = coverAfter - coverBefore;
2574 Number const lost = requested - actual;
2575 BEAST_EXPECT(lost >=
Number{0});
2576 BEAST_EXPECT(lost < oneUlp);
2581 testcase(
"Cover precision guard: Withdraw");
2582 Env env{*
this, features};
2583 auto const [brokerKeylet, iou] = setup(env);
2585 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2586 auto const aliceBalanceBefore = env.
balance(alice, iou);
2587 env(coverWithdraw(alice, brokerKeylet.key, subUlpAmt),
Ter(expected));
2591 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
2592 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
2593 BEAST_EXPECT(env.
balance(alice, iou) == aliceBalanceBefore);
2598 testcase(
"Cover precision guard: Clawback");
2599 Env env{*
this, features};
2600 auto const [brokerKeylet, iou] = setup(env);
2602 auto const coverBefore = env.
le(brokerKeylet)->at(sfCoverAvailable);
2603 env(coverClawback(issuer),
2604 kLoanBrokerId(brokerKeylet.key),
2610 if (
auto const broker = env.
le(brokerKeylet); BEAST_EXPECT(broker))
2611 BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore);
2618 testcase(
"Cover precision guard: MPT min amount passes");
2621 env.
fund(
XRP(100'000), issuer, alice);
2625 mptt.
create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
2632 env(
pay(issuer, alice, mptAsset(100)));
2635 Vault const vault{env};
2636 auto [
createTx, vaultKeylet] = vault.create({.owner = alice, .asset = mptAsset});
2641 env(
set(alice, vaultKeylet.key));
2644 env(coverDeposit(alice, brokerKeylet.key, mptAsset(10)));
2647 env(coverDeposit(alice, brokerKeylet.key, mptAsset(1)),
Ter(
tesSUCCESS));
2650 env(coverWithdraw(alice, brokerKeylet.key, mptAsset(1)),
Ter(
tesSUCCESS));
2653 env(coverClawback(issuer),
2654 kLoanBrokerId(brokerKeylet.key),
2662 runTestCases(
all_ - fixCleanup3_2_0);
A generic endpoint for log messages.
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.
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 &)
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.
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...
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)
constexpr TenthBips16 kMaxManagementFeeRate(unsafeCast< std::uint16_t >(percentageToTenthBips(10).value()))
The maximum management fee rate allowed by a loan broker in 1/10 bips.
constexpr TenthBips32 kMaxCoverRate
The maximum coverage rate required of a loan broker in 1/10 bips.
Keylet computation functions.
Keylet loanBroker(AccountID const &owner, std::uint32_t seq) noexcept
Keylet loan(uint256 const &loanBrokerID, std::uint32_t loanSeq) noexcept
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Deposit preauthorize operations.
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
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.