163 Account const issuer{
"issuer"};
165 Account const depositor{
"depositor"};
166 Account const charlie{
"charlie"};
169 auto const testSequence = [&,
this](
174 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
175 tx[sfData] =
"AFEED00E";
176 tx[sfAssetsMaximum] = asset(100).number();
179 BEAST_EXPECT(env.le(
keylet));
182 auto const [share, vaultAccount] =
184 auto const vault = env.le(
keylet);
185 BEAST_EXPECT(vault !=
nullptr);
186 if (!asset.integral())
188 BEAST_EXPECT(vault->at(sfScale) == 6);
192 BEAST_EXPECT(vault->at(sfScale) == 0);
195 BEAST_EXPECT(shares !=
nullptr);
196 if (!asset.integral())
198 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
202 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
204 return {
MPTIssue(vault->at(sfShareMPTID)),
Account(
"vault", vault->at(sfAccount))};
206 auto const shares = share.raw().get<
MPTIssue>();
207 env.memoize(vaultAccount);
212 env.fund(XRP(1000), alice, erin);
213 env(fset(alice, asfDepositAuth));
217 testcase(prefix +
" fail to deposit more than assets held");
218 auto tx = vault.deposit(
219 {.depositor = depositor, .id =
keylet.key, .amount = asset(10000)});
225 testcase(prefix +
" deposit non-zero amount");
227 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
230 BEAST_EXPECT(env.balance(depositor, shares) == share(50 *
scale));
234 testcase(prefix +
" deposit non-zero amount again");
236 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
239 BEAST_EXPECT(env.balance(depositor, shares) == share(100 *
scale));
243 testcase(prefix +
" fail to delete non-empty vault");
244 auto tx = vault.del({.owner = owner, .id =
keylet.key});
250 testcase(prefix +
" fail to update because wrong owner");
251 auto tx = vault.set({.owner = issuer, .id =
keylet.key});
252 tx[sfAssetsMaximum] = asset(50).number();
258 testcase(prefix +
" fail to set maximum lower than current amount");
259 auto tx = vault.set({.owner = owner, .id =
keylet.key});
260 tx[sfAssetsMaximum] = asset(50).number();
266 testcase(prefix +
" set maximum higher than current amount");
267 auto tx = vault.set({.owner = owner, .id =
keylet.key});
268 tx[sfAssetsMaximum] = asset(150).number();
274 testcase(prefix +
" set maximum is idempotent, set it again");
275 auto tx = vault.set({.owner = owner, .id =
keylet.key});
276 tx[sfAssetsMaximum] = asset(150).number();
283 auto tx = vault.set({.owner = owner, .id =
keylet.key});
290 testcase(prefix +
" fail to set domain on public vault");
291 auto tx = vault.set({.owner = owner, .id =
keylet.key});
298 testcase(prefix +
" fail to deposit more than maximum");
300 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
306 testcase(prefix +
" reset maximum to zero i.e. not enforced");
307 auto tx = vault.set({.owner = owner, .id =
keylet.key});
308 tx[sfAssetsMaximum] = asset(0).number();
314 testcase(prefix +
" fail to withdraw more than assets held");
315 auto tx = vault.withdraw(
316 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
322 testcase(prefix +
" deposit some more");
324 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
327 BEAST_EXPECT(env.balance(depositor, shares) == share(200 *
scale));
331 testcase(prefix +
" clawback some");
333 auto tx = vault.clawback(
334 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(10)});
337 if (!asset.raw().native())
339 BEAST_EXPECT(env.balance(depositor, shares) == share(190 *
scale));
346 auto tx = vault.clawback({.issuer = issuer, .id =
keylet.key, .holder = depositor});
349 if (!asset.raw().native())
351 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
354 auto tx = vault.clawback(
358 .amount = asset(10)});
364 auto tx = vault.withdraw(
365 {.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
372 if (!asset.raw().native())
374 testcase(prefix +
" deposit again");
376 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(200)});
379 BEAST_EXPECT(env.balance(depositor, shares) == share(200 *
scale));
383 testcase(prefix +
" deposit/withdrawal same or less than fee");
384 auto const amount = env.current()->fees().base;
387 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount});
391 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = amount});
395 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount});
400 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = amount});
401 tx[sfDestination] = charlie.human();
406 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount - 1});
411 {.depositor = depositor, .id =
keylet.key, .amount = amount - 1});
417 testcase(prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
418 auto tx = vault.withdraw(
419 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
420 tx[sfDestination] = alice.human();
426 testcase(prefix +
" fail to withdraw to zero destination");
427 auto tx = vault.withdraw(
428 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
429 tx[sfDestination] =
"0";
434 if (!asset.raw().native())
436 testcase(prefix +
" fail to withdraw to 3rd party no authorization");
437 auto tx = vault.withdraw(
438 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
439 tx[sfDestination] = erin.human();
445 testcase(prefix +
" fail to withdraw to 3rd party lsfRequireDestTag");
446 auto tx = vault.withdraw(
447 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
448 tx[sfDestination] = dave.human();
454 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
456 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
457 tx[sfDestination] = dave.human();
458 tx[sfDestinationTag] =
"0";
464 testcase(prefix +
" deposit again");
465 auto tx = vault.deposit({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
471 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
473 vault.withdraw({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
479 testcase(prefix +
" withdraw with tag");
481 vault.withdraw({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
482 tx[sfDestinationTag] =
"0";
488 testcase(prefix +
" withdraw to authorized 3rd party");
490 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
491 tx[sfDestination] = charlie.human();
494 BEAST_EXPECT(env.balance(depositor, shares) == share(100 *
scale));
498 testcase(prefix +
" withdraw to issuer");
500 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
501 tx[sfDestination] = issuer.human();
504 BEAST_EXPECT(env.balance(depositor, shares) == share(50 *
scale));
507 if (!asset.raw().native())
509 testcase(prefix +
" issuer deposits");
511 vault.deposit({.depositor = issuer, .id =
keylet.key, .amount = asset(10)});
514 BEAST_EXPECT(env.balance(issuer, shares) == share(10 *
scale));
516 testcase(prefix +
" issuer withdraws");
518 {.depositor = issuer, .id =
keylet.key, .amount = share(10 *
scale)});
521 BEAST_EXPECT(env.balance(issuer, shares) == share(0 *
scale));
525 testcase(prefix +
" withdraw remaining assets");
527 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
530 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
532 if (!asset.raw().native())
534 auto tx = vault.clawback(
538 .amount = asset(0)});
544 auto tx = vault.withdraw(
545 {.depositor = depositor, .id =
keylet.key, .amount = share(10)});
551 if (!asset.integral())
553 testcase(prefix +
" temporary authorization for 3rd party");
554 env(trust(erin, asset(1000)));
555 env(trust(issuer, asset(0), erin, tfSetfAuth));
556 env(pay(issuer, erin, asset(10)));
559 auto tx = vault.deposit({.depositor = erin, .id =
keylet.key, .amount = asset(10)});
563 auto tx = pay(erin, depositor, share(10 *
scale));
571 {.depositor = depositor, .id = keylet.key, .amount = asset(1)}));
578 testcase(prefix +
" withdraw to authorized 3rd party");
581 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
582 tx[sfDestination] = erin.human();
587 env(pay(erin, issuer, asset(10)));
590 testcase(prefix +
" fail to pay to unauthorized 3rd party");
591 env(trust(erin, asset(0)));
595 env(pay(depositor, erin, share(1)), Ter{
tecNO_LINE});
599 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(1)});
605 testcase(prefix +
" fail to delete because wrong owner");
606 auto tx = vault.del({.owner = issuer, .id =
keylet.key});
612 testcase(prefix +
" delete empty vault");
613 auto tx = vault.del({.owner = owner, .id =
keylet.key});
616 BEAST_EXPECT(!env.le(
keylet));
620 auto testCases = [&,
this](
622 Env env{*
this, testableAmendments()};
625 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
627 env(fset(issuer, asfAllowTrustLineClawback));
628 env(fset(issuer, asfRequireAuth));
629 env(fset(dave, asfRequireDest));
631 env.require(Flags(issuer, asfAllowTrustLineClawback));
632 env.require(Flags(issuer, asfRequireAuth));
635 testSequence(prefix, env, vault, asset);
640 testCases(
"IOU", [&](Env& env) ->
Asset {
642 env(trust(owner, asset(1000)));
643 env(trust(depositor, asset(1000)));
644 env(trust(charlie, asset(1000)));
645 env(trust(dave, asset(1000)));
646 env(trust(issuer, asset(0), owner, tfSetfAuth));
647 env(trust(issuer, asset(0), depositor, tfSetfAuth));
648 env(trust(issuer, asset(0), charlie, tfSetfAuth));
649 env(trust(issuer, asset(0), dave, tfSetfAuth));
650 env(pay(issuer, depositor, asset(1000)));
655 testCases(
"MPT", [&](Env& env) ->
Asset {
656 MPTTester mptt{env, issuer, kMptInitNoFund};
657 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
659 mptt.authorize({.account = depositor});
660 mptt.authorize({.account = charlie});
661 mptt.authorize({.account = dave});
662 env(pay(issuer, depositor, asset(1000)));
678 auto testCase = [&,
this](
685 CaseArgs args = {}) {
686 Env env{*
this, args.features};
687 Account const issuer{
"issuer"};
690 env.fund(XRP(1000), issuer, owner);
693 env(fset(issuer, asfAllowTrustLineClawback));
694 env(fset(issuer, asfRequireAuth));
698 env(trust(owner, asset(1000)));
699 env(trust(issuer, asset(0), owner, tfSetfAuth));
700 env(pay(issuer, owner, asset(1000)));
703 test(env, issuer, owner, asset, vault);
707 return [&, resultAfterCreate](
713 testcase(
"disabled single asset vault");
715 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
719 auto tx = vault.set({.owner = owner, .id =
keylet.key});
720 env(tx, kData(
"test"), Ter{resultAfterCreate});
725 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
726 env(tx, Ter{resultAfterCreate});
731 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
732 env(tx, Ter{resultAfterCreate});
736 auto tx = vault.clawback(
737 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(10)});
738 env(tx, Ter{resultAfterCreate});
742 auto tx = vault.del({.owner = owner, .id =
keylet.key});
743 env(tx, Ter{resultAfterCreate});
748 testCase(testDisabled(), {.features = testableAmendments() - featureSingleAssetVault});
750 testCase(testDisabled(
tecNO_ENTRY), {.features = testableAmendments() - featureMPTokensV1});
758 testcase(
"disabled permissioned domains");
760 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
763 tx[sfFlags] = tx[sfFlags].asUInt() | tfVaultPrivate;
768 auto tx = vault.set({.owner = owner, .id =
keylet.key});
769 env(tx, kData(
"Test"));
775 {.features = testableAmendments() - featurePermissionedDomains});
777 testCase([&](Env& env,
784 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
785 tx[sfFlags] = tfClearDeepFreeze;
789 auto tx = vault.set({.owner = owner, .id =
keylet.key});
790 tx[sfFlags] = tfClearDeepFreeze;
796 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
797 tx[sfFlags] = tfClearDeepFreeze;
803 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
804 tx[sfFlags] = tfClearDeepFreeze;
809 auto tx = vault.clawback(
810 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(10)});
811 tx[sfFlags] = tfClearDeepFreeze;
816 auto tx = vault.del({.owner = owner, .id =
keylet.key});
817 tx[sfFlags] = tfClearDeepFreeze;
822 testCase([&](Env& env,
829 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
834 auto tx = vault.set({.owner = owner, .id =
keylet.key});
841 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
848 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
854 auto tx = vault.clawback(
855 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(10)});
861 auto tx = vault.del({.owner = owner, .id =
keylet.key});
869 testcase(
"disabled permissioned domain");
871 auto [tx,
keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
876 auto tx = vault.set({.owner = owner, .id =
keylet.key});
882 auto tx = vault.set({.owner = owner, .id =
keylet.key});
883 tx[sfDomainID] =
"0";
887 {.features = (testableAmendments()) - featurePermissionedDomains});
889 testCase([&](Env& env,
896 auto [tx,
keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
899 auto tx = vault.set({
908 vault.deposit({.depositor = owner, .id =
beast::kZero, .amount = asset(10)});
914 vault.withdraw({.depositor = owner, .id =
beast::kZero, .amount = asset(10)});
919 auto tx = vault.clawback(
920 {.issuer = issuer, .id =
beast::kZero, .holder = owner, .amount = asset(10)});
925 auto tx = vault.del({
935 testcase(
"withdraw to bad destination");
937 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
941 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
942 tx[jss::Destination] =
"0";
952 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
958 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
965 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
969 auto const sleVault = env.le(
keylet);
970 BEAST_EXPECT(sleVault);
971 BEAST_EXPECT((*sleVault)[sfScale] == 18);
975 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
979 auto const sleVault = env.le(
keylet);
980 BEAST_EXPECT(sleVault);
981 BEAST_EXPECT((*sleVault)[sfScale] == 0);
985 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
988 auto const sleVault = env.le(
keylet);
989 BEAST_EXPECT(sleVault);
990 BEAST_EXPECT((*sleVault)[sfScale] == 6);
996 testcase(
"create or set invalid data");
998 auto [tx1,
keylet] = vault.create({.owner = owner, .asset = asset});
1014 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1020 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1031 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1034 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1041 testcase(
"create with invalid metadata");
1043 auto [tx1,
keylet] = vault.create({.owner = owner, .asset = asset});
1047 tx[sfMPTokenMetadata] =
"";
1064 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1067 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1075 testcase(
"invalid deposit amount");
1077 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1080 auto tx = vault.deposit(
1087 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(0)});
1094 testcase(
"invalid set immutable flag");
1096 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1099 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1100 tx[sfFlags] = tfVaultPrivate;
1107 testcase(
"invalid withdraw amount");
1109 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1112 auto tx = vault.withdraw(
1119 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(0)});
1124 testCase([&](Env& env,
1131 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1136 auto tx = vault.clawback(
1137 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(50)});
1142 auto tx = vault.clawback(
1155 auto [tx1,
keylet] = vault.create({.owner = owner, .asset = asset});
1159 tx[sfWithdrawalPolicy] = 0;
1177 tx[sfFlags] = tfVaultPrivate;
1178 tx[sfDomainID] =
"0";
1189 testcase(
"closed-ended VaultCreate");
1192 auto const withEnv = [
this](
FeatureBitset features,
auto&& body) {
1193 Env env{*
this, features};
1195 env.
fund(XRP(1000), owner);
1198 body(env, owner, vault);
1208 testableAmendments() - featureLendingProtocolV1_1,
1210 auto const sub = env.now().time_since_epoch().count() + 60;
1211 auto [tx,
keylet] = vault.create(
1214 .vaultKind = closedEnded,
1215 .subscriptionDate = sub,
1216 .redemptionDate = sub + minPeriod});
1224 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1225 auto const sub = env.now().time_since_epoch().count() + 60;
1226 auto const red = sub + 86400;
1227 auto [tx,
keylet] = vault.create(
1230 .vaultKind = closedEnded,
1231 .subscriptionDate = sub,
1232 .redemptionDate = red});
1235 auto const sle = env.le(
keylet);
1236 if (BEAST_EXPECT(sle))
1238 BEAST_EXPECT(sle->at(sfVaultKind) == closedEnded);
1239 BEAST_EXPECT(sle->at(sfSubscriptionDate) == sub);
1240 BEAST_EXPECT(sle->at(sfRedemptionDate) == red);
1245 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1246 auto const sub = env.now().time_since_epoch().count() + 60;
1247 auto [tx,
keylet] = vault.create(
1250 .vaultKind = closedEnded,
1251 .redemptionDate = sub + minPeriod});
1254 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1255 auto const sub = env.now().time_since_epoch().count() + 60;
1256 auto [tx,
keylet] = vault.create(
1259 .vaultKind = closedEnded,
1260 .subscriptionDate = sub});
1274 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1275 auto const nowSec = env.now().time_since_epoch().count();
1276 auto [tx,
keylet] = vault.create(
1279 .vaultKind = closedEnded,
1280 .subscriptionDate = nowSec,
1281 .redemptionDate = nowSec + minPeriod});
1291 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1292 auto const sub = env.now().time_since_epoch().count() + 60;
1293 auto [tx,
keylet] = vault.create(
1296 .vaultKind = closedEnded,
1297 .subscriptionDate = sub,
1298 .redemptionDate = sub + minPeriod - 1});
1301 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1302 auto const sub = env.now().time_since_epoch().count() + 60;
1303 auto [tx,
keylet] = vault.create(
1306 .vaultKind = closedEnded,
1307 .subscriptionDate = sub,
1308 .redemptionDate = sub});
1311 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1312 auto const sub = env.now().time_since_epoch().count() + 60;
1313 auto [tx,
keylet] = vault.create(
1316 .vaultKind = closedEnded,
1317 .subscriptionDate = sub,
1318 .redemptionDate = sub - 1});
1323 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1324 auto const sub = env.now().time_since_epoch().count() + 60;
1325 auto [tx,
keylet] = vault.create(
1328 .vaultKind = closedEnded,
1329 .subscriptionDate = sub,
1330 .redemptionDate = sub + maxPeriod});
1336 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1337 auto const sub = env.now().time_since_epoch().count() + 60;
1338 auto [tx,
keylet] = vault.create(
1341 .vaultKind = closedEnded,
1342 .subscriptionDate = sub,
1343 .redemptionDate = sub + maxPeriod + 1});
1349 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1350 auto const sub = env.now().time_since_epoch().count() + 60;
1351 auto const red = sub + minPeriod;
1352 auto [tx,
keylet] = vault.create(
1355 .vaultKind = closedEnded,
1356 .subscriptionDate = sub,
1357 .redemptionDate = red});
1360 auto const sle = env.le(
keylet);
1361 if (BEAST_EXPECT(sle))
1363 BEAST_EXPECT(sle->at(sfRedemptionDate) == red);
1369 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1370 auto const sub = env.now().time_since_epoch().count() + 60;
1371 auto const red = sub + maxPeriod - 1;
1372 auto [tx,
keylet] = vault.create(
1375 .vaultKind = closedEnded,
1376 .subscriptionDate = sub,
1377 .redemptionDate = red});
1380 auto const sle = env.le(
keylet);
1381 if (BEAST_EXPECT(sle))
1383 BEAST_EXPECT(sle->at(sfRedemptionDate) == red);
1389 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1390 auto const sub = env.now().time_since_epoch().count() + 60;
1392 vault.create({.owner = owner, .asset = asset, .subscriptionDate = sub});
1395 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1396 auto const sub = env.now().time_since_epoch().count() + 60;
1398 vault.create({.owner = owner, .asset = asset, .redemptionDate = sub + minPeriod});
1403 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1404 auto [tx,
keylet] = vault.create(
1407 .vaultKind =
static_cast<std::uint8_t>(closedEnded + 1)});
1412 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1413 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1416 auto const sle = env.le(
keylet);
1417 if (BEAST_EXPECT(sle))
1419 BEAST_EXPECT(!sle->isFieldPresent(sfVaultKind));
1420 BEAST_EXPECT(!sle->isFieldPresent(sfSubscriptionDate));
1421 BEAST_EXPECT(!sle->isFieldPresent(sfRedemptionDate));
1427 withEnv(testableAmendments(), [&](Env& env,
Account const& owner,
Vault& vault) {
1428 auto [tx,
keylet] = vault.create(
1434 auto const sle = env.le(
keylet);
1435 if (BEAST_EXPECT(sle))
1439 BEAST_EXPECT(!sle->isFieldPresent(sfVaultKind));
1440 BEAST_EXPECT(!sle->isFieldPresent(sfSubscriptionDate));
1441 BEAST_EXPECT(!sle->isFieldPresent(sfRedemptionDate));
1689 testcase(
"closed-ended vault lifecycle (subscribe → invest → redeem)");
1691 using namespace loan_broker;
1692 using namespace loan;
1694 Env env{*
this, testableAmendments()};
1698 Account const borrower{
"borrower"};
1699 env.
fund(XRP(10'000), owner, alice, bob, borrower);
1706 auto const [vault,
keylet, sub, red] =
1709 auto const sleCreate = env.le(
keylet);
1710 BEAST_EXPECT(sleCreate);
1711 MPTIssue const shares{sleCreate->at(sfShareMPTID)};
1713 auto const balancesEq = [&](
STAmount const& available,
STAmount const& total) {
1714 auto const sle = env.le(
keylet);
1715 BEAST_EXPECT(sle->at(sfAssetsAvailable) == available);
1716 BEAST_EXPECT(sle->at(sfAssetsTotal) == total);
1718 auto const availableEq = [&](
STAmount const& expected) { balancesEq(expected, expected); };
1725 std::uint64_t const actual = sle ? sle->getFieldU64(sfMPTAmount) : 0u;
1726 BEAST_EXPECT(actual == expected);
1732 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1739 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = XRP(100).value()}));
1741 sharesEq(alice, 100'000'000);
1742 availableEq(XRP(100).value());
1745 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = XRP(200).value()}));
1747 sharesEq(bob, 200'000'000);
1748 availableEq(XRP(300).value());
1751 env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = XRP(25).value()}));
1753 sharesEq(alice, 75'000'000);
1754 availableEq(XRP(275).value());
1758 auto const brokerKeylet =
1760 env(loan_broker::set(owner,
keylet.key));
1764 env.close(
tp{
d{sub + 1}});
1767 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = XRP(10).value()}),
1771 env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = XRP(10).value()}),
1779 env(loan::set(borrower, brokerKeylet.key, XRP(60).value()),
1782 kPaymentInterval(60),
1784 Sig(sfCounterpartySignature, owner),
1785 Fee(env.current()->fees().base * 2));
1788 BEAST_EXPECT(sleBroker);
1790 BEAST_EXPECT(env.le(loanKeylet));
1791 balancesEq(XRP(215).value(), XRP(275).value());
1795 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1802 sharesEq(alice, 75'000'000);
1803 sharesEq(bob, 200'000'000);
1806 env.close(
tp{
d{red}});
1810 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = XRP(10).value()}),
1815 env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = XRP(75).value()}));
1818 balancesEq(XRP(140).value(), XRP(200).value());
1825 env(vault.withdraw({.depositor = bob, .id = keylet.key, .amount = XRP(200).value()}),
1826 Ter{tecINSUFFICIENT_FUNDS});
1828 env(vault.withdraw({.depositor = bob, .id = keylet.key, .amount = XRP(140).value()}));
1830 sharesEq(bob, 60'000'000);
1831 balancesEq(XRP(0).value(), XRP(60).value());
1835 auto const sleFinal = env.le(
keylet);
1836 if (BEAST_EXPECT(sleFinal))
1838 BEAST_EXPECT(sleFinal->at(sfVaultKind) == closedEnded);
1839 BEAST_EXPECT(sleFinal->at(sfSubscriptionDate) == sub);
1840 BEAST_EXPECT(sleFinal->at(sfRedemptionDate) == red);
2594 bool enableClawback =
true;
2596 int initialXRP = 1000;
2600 auto testCase = [
this](
2608 MPTTester& mptt)>
test,
2609 CaseArgs args = {}) {
2610 Env env{*
this, args.features};
2611 Account const issuer{
"issuer"};
2613 Account const depositor{
"depositor"};
2614 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
2618 MPTTester mptt{env, issuer, kMptInitNoFund};
2621 {.flags = tfMPTCanTransfer | tfMPTCanLock |
2622 (args.enableClawback ? tfMPTCanClawback : kNone) |
2623 (args.requireAuth ? tfMPTRequireAuth : kNone)});
2625 mptt.authorize({.account = owner});
2626 mptt.authorize({.account = depositor});
2627 if (args.requireAuth)
2629 mptt.authorize({.account = issuer, .holder = owner});
2630 mptt.authorize({.account = issuer, .holder = depositor});
2633 env(pay(issuer, depositor, asset(1000)));
2636 test(env, issuer, owner, depositor, asset, vault, mptt);
2647 testcase(
"MPT nothing to clawback from");
2648 auto tx = vault.clawback(
2651 .holder = depositor,
2652 .amount = asset(10)});
2664 testcase(
"MPT global lock blocks create");
2665 mptt.set({.account = issuer, .flags = tfMPTLock});
2666 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2678 testcase(
"MPT only issuer can clawback");
2680 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2684 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
2689 auto tx = vault.clawback({
2690 .issuer = depositor,
2692 .holder = depositor,
2698 auto tx = vault.clawback({
2701 .holder = depositor,
2716 testcase(
"MPT depositor without MPToken, auth required");
2718 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2723 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
2729 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
2733 auto const sleMPT1 = env.le(mptoken);
2734 BEAST_EXPECT(sleMPT1 ==
nullptr);
2736 tx = vault.withdraw(
2737 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
2741 auto const sleMPT2 = env.le(mptoken);
2742 BEAST_EXPECT(sleMPT2 ==
nullptr);
2747 Account const charlie{
"charlie"};
2748 env.fund(XRP(1000), charlie);
2751 tx = vault.withdraw(
2752 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
2753 tx[sfDestination] = charlie.human();
2757 {.requireAuth =
true});
2768 testcase(
"MPT depositor without MPToken, no auth required");
2770 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2777 {.depositor = depositor,
2779 .amount = asset(1000)});
2785 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
2789 auto const sleMPT1 = env.le(mptoken);
2790 BEAST_EXPECT(sleMPT1 ==
nullptr);
2792 tx = vault.withdraw(
2793 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
2797 auto const sleMPT2 = env.le(mptoken);
2798 BEAST_EXPECT(sleMPT2 !=
nullptr);
2799 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
2804 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
2808 auto const sleMPT1 = env.le(mptoken);
2809 BEAST_EXPECT(sleMPT1 ==
nullptr);
2811 tx = vault.withdraw(
2812 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
2813 tx[sfDestination] = owner.human();
2817 auto const sleMPT2 = env.le(mptoken);
2818 BEAST_EXPECT(sleMPT2 ==
nullptr);
2821 {.requireAuth =
false});
2824 Env
const env{*
this, testableAmendments()};
2826 env.current()->fees().accountReserve(0, 1).drops() /
kDropsPerXrp.drops(),
2827 env.current()->fees().increment.drops() /
kDropsPerXrp.drops()};
2839 testcase(
"MPT fail reserve to re-create MPToken");
2841 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2847 env(pay(depositor, owner, asset(1000)));
2851 {.depositor = owner,
2853 .amount = asset(1000)});
2859 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
2863 auto const sleMPT = env.le(mptoken);
2864 BEAST_EXPECT(sleMPT ==
nullptr);
2867 env(ticket::create(owner, 1));
2871 tx = vault.withdraw(
2872 {.depositor = owner, .id =
keylet.key, .amount = asset(100)});
2876 env(pay(depositor, owner, XRP(incReserve)));
2884 {.requireAuth =
false, .initialXRP = acctReserve + (incReserve * 4) + 1});
2896 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2900 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
2905 auto tx = vault.clawback(
2906 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
2910 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
2914 auto [tx,
keylet] = vault.create({.owner = depositor, .asset = asset});
2920 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
2926 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
2931 auto tx = vault.clawback(
2932 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
2936 env(vault.del({.owner = owner, .id = keylet.key}));
2947 testcase(
"MPT vault owner can receive shares unless unauthorized");
2949 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2953 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
2958 auto const vault = env.le(
keylet);
2959 return vault->at(sfShareMPTID);
2965 env(pay(depositor, owner, shares(1)));
2968 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = shares(1)});
2973 env(pay(depositor, owner, shares(1)));
2976 tx = vault.clawback(
2977 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(0)});
2982 env(pay(depositor, owner, shares(1)));
2986 env(pay(owner, depositor, shares(1)));
2992 jv[sfAccount] = owner.human();
2993 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2994 jv[sfFlags] = tfMPTUnauthorize;
2995 jv[sfTransactionType] = jss::MPTokenAuthorize;
3001 tx = pay(depositor, owner, shares(1));
3006 tx = vault.clawback(
3007 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
3012 env(vault.del({.owner = owner, .id = keylet.key}));
3028 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3033 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
3038 auto tx = vault.clawback(
3041 .holder = depositor,
3042 .amount = asset(0)});
3046 {.enableClawback =
false});
3057 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3060 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
3064 mptt.authorize({.account = issuer, .holder = depositor, .flags = tfMPTUnauthorize});
3068 auto tx = vault.withdraw(
3069 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
3073 tx[sfDestination] = issuer.human();
3077 tx[sfDestination] = owner.human();
3085 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
3091 tx = vault.clawback(
3092 {.issuer = issuer, .id =
keylet.key, .holder = issuer, .amount = asset(800)});
3096 tx = vault.clawback(
3097 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(800)});
3101 env(vault.del({.owner = owner, .id = keylet.key}));
3107 Env env{*
this, testableAmendments()};
3109 Account const issuer{
"issuer"};
3110 env.fund(XRP(1000000), owner, issuer);
3112 Vault const vault{env};
3114 MPTTester mptt{env, issuer, kMptInitNoFund};
3116 {.flags = tfMPTCanTransfer | tfMPTCanLock | lsfMPTCanClawback | tfMPTRequireAuth});
3117 mptt.authorize({.account = owner});
3118 mptt.authorize({.account = issuer, .holder = owner});
3120 env(pay(issuer, owner, asset(100)));
3121 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
3125 auto const shares = [&env,
keylet = k1,
this]() ->
Asset {
3126 auto const vault = env.le(
keylet);
3127 BEAST_EXPECT(vault !=
nullptr);
3128 return MPTIssue(vault->at(sfShareMPTID));
3131 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
3137 testcase(
"MPT locked: vault shares inherit underlying lock");
3139 Env env{*
this, testableAmendments()};
3140 Account const issuer{
"issuer"};
3145 env.fund(XRP(10'000), issuer, owner, alice, bob, carol);
3147 Vault const vault{env};
3152 .holders = {owner, alice, bob, carol},
3153 .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanLock}};
3154 env(pay(issuer, alice, asset(1'000)));
3155 env(pay(issuer, bob, asset(1'000)));
3158 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3162 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(500)}));
3164 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(500)}));
3168 auto const sle = env.le(
keylet);
3169 BEAST_EXPECT(sle !=
nullptr);
3170 return MPTIssue(sle->at(sfShareMPTID));
3172 auto const shareMptID = shares.raw().get<
MPTIssue>().getMptID();
3173 auto const shareBalance = [&](
Account const& account) {
3175 return sle ? sle->at(sfMPTAmount) : 0;
3180 env(pay(alice, bob, shares(1)));
3185 env(offer(alice, XRP(1), shares(1)));
3189 asset.set({.account = issuer, .flags = tfMPTLock});
3194 BEAST_EXPECT(shareBalance(alice) == 499);
3195 BEAST_EXPECT(shareBalance(bob) == 501);
3196 env(pay(alice, bob, shares(1)), Ter{
tecLOCKED});
3198 BEAST_EXPECT(shareBalance(alice) == 499);
3199 BEAST_EXPECT(shareBalance(bob) == 501);
3203 env(pay(carol, bob, shares(1)),
3205 Path(BookSpec{shares.raw()}),
3208 BEAST_EXPECT(shareBalance(alice) == 499);
3209 BEAST_EXPECT(shareBalance(bob) == 501);
3210 BEAST_EXPECT(expectOffers(env, alice, 1));
3214 testcase(
"MPT CanTrade governance: share inherits underlying on DEX and AMM");
3216 Env env{*
this, testableAmendments()};
3217 Account const issuer{
"issuer"};
3221 env.fund(XRP(100'000), issuer, owner, alice, bob);
3223 Vault const vault{env};
3225 MPTTester mptt{env, issuer, kMptInitNoFund};
3226 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
3228 mptt.authorize({.account = owner});
3229 mptt.authorize({.account = alice});
3230 mptt.authorize({.account = bob});
3231 env(pay(issuer, alice, asset(10'000)));
3232 env(pay(issuer, bob, asset(10'000)));
3235 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3240 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(5'000)}));
3241 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(5'000)}));
3245 auto const sle = env.le(
keylet);
3246 BEAST_EXPECT(sle !=
nullptr);
3247 return MPTIssue(sle->at(sfShareMPTID));
3257 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(100)}));
3262 env(pay(alice, bob, shares(1)));
3266 env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = asset(100)}));
3270 mptt.set({.flags = tfMPTSetCanTrade});
3273 env(offer(alice, XRP(1), asset(10)));
3274 env(offer(alice, XRP(1), shares(1)));
3277 AMM
const ammUnderlying(env, alice, XRP(1'000), asset(1'000));
3281 testcase(
"MPT OutstandingAmount > MaximumAmount");
3283 Env env{*
this, testableAmendments() | featureSingleAssetVault};
3285 Account const issuer{
"issuer"};
3286 env.fund(XRP(1'000), alice, issuer);
3288 Vault const vault{env};
3290 MPTTester
const btc({.env = env, .issuer = issuer, .holders = {alice}, .maxAmt = 100});
3292 auto [tx, k] = vault.create({.owner = issuer, .asset = btc});
3296 tx = vault.deposit({.depositor = issuer, .id = k.key, .amount = btc(110)});
3303 env(pay(issuer, alice, btc(100)));
3306 tx = vault.deposit({.depositor = issuer, .id = k.key, .amount = btc(100)});
3311 tx = vault.deposit({.depositor = alice, .id = k.key, .amount = btc(100)});
3325 int initialXRP = 1000;
3328 bool charlieRipple =
true;
3332 auto testCase = [&,
this](
3342 CaseArgs args = {}) {
3343 Env env{*
this, args.features};
3345 Account const issuer{
"issuer"};
3346 Account const charlie{
"charlie"};
3348 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
3349 env(fset(issuer, asfAllowTrustLineClawback));
3353 env.trust(asset(1000), owner);
3354 env(pay(issuer, owner, asset(args.initialIOU)));
3356 if (!args.charlieRipple)
3358 env(fset(issuer, 0, asfDefaultRipple));
3360 env.trust(asset(1000), charlie);
3362 env(pay(issuer, charlie, asset(args.initialIOU)));
3364 env(fset(issuer, asfDefaultRipple));
3368 env.trust(asset(1000), charlie);
3371 env(rate(issuer, args.transferRate));
3378 return env.le(
keylet)->at(sfShareMPTID);
3381 test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId);
3393 testcase(
"IOU cannot use different asset");
3396 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3402 auto tx = [&, account = vaultAccount(
keylet)]() {
3404 jv[jss::Account] = issuer.human();
3406 auto& ja = jv[jss::LimitAmount] =
3408 ja[jss::issuer] =
toBase58(account);
3410 jv[jss::TransactionType] = jss::TrustSet;
3411 jv[jss::Flags] = tfSetFreeze;
3419 auto tx = vault.deposit({.depositor = issuer, .id =
keylet.key, .amount = foo(20)});
3426 vault.withdraw({.depositor = issuer, .id =
keylet.key, .amount = foo(20)});
3431 env(vault.del({.owner = owner, .id = keylet.key}));
3445 testcase(
"IOU transfer fees not applied");
3447 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3451 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3458 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
3459 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(100));
3462 auto tx = vault.clawback(
3463 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(50)});
3469 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
3470 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(50));
3473 {.depositor = owner, .id = keylet.key, .amount = share(20'000'000)}));
3476 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
3477 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(30));
3480 auto tx = vault.withdraw(
3481 {.depositor = owner, .id =
keylet.key, .amount = share(30'000'000)});
3482 tx[sfDestination] = charlie.human();
3487 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
3488 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
3489 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(0));
3491 env(vault.del({.owner = owner, .id = keylet.key}));
3494 CaseArgs{.transferRate = 1.25});
3505 testcase(
"IOU no trust line to 3rd party");
3507 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3511 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3515 env.fund(XRP(1000), erin);
3521 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
3522 tx[sfDestination] = erin.human();
3537 testcase(
"IOU no trust line to depositor");
3539 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3544 env.trust(asset(0), owner);
3547 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
3551 BEAST_EXPECT(trustline ==
nullptr);
3556 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
3574 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3581 env(trust(issuer, vaultAccount(
keylet)[
"IOU"], tfSetNoRipple));
3585 auto tx = vault.deposit(
3586 {.depositor = charlie, .id =
keylet.key, .amount = asset(100)});
3594 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(100)});
3599 auto tx2 = vault.withdraw(
3600 {.depositor = owner, .id =
keylet.key, .amount = shares(100)});
3601 tx2[sfDestination] = charlie.human();
3608 tx[sfAccount] = charlie.human();
3609 tx[sfMPTokenIssuanceID] =
3611 tx[sfTransactionType] = jss::MPTokenAuthorize;
3625 env(pay(owner, charlie, shares(100)), Ter{
tecPATH_DRY});
3629 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(100)});
3634 env(vault.del({.owner = owner, .id = keylet.key}));
3636 {.charlieRipple =
false});
3644 auto const& vaultAccount,
3648 testcase(
"IOU calculation rounding");
3650 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3655 auto const startingOwnerBalance = env.balance(owner, asset);
3656 BEAST_EXPECT((startingOwnerBalance.value() ==
STAmount{asset, 11875, -2}));
3662 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3664 auto const tx1 = vault.deposit(
3665 {.depositor = owner, .id =
keylet.key, .amount = asset(
Number(375, -2))});
3666 for (
auto i = 0; i < 5; ++i)
3673 STAmount const xfer{asset, 1185, -1};
3674 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value() - xfer);
3675 BEAST_EXPECT(env.balance(vaultAccount(
keylet), asset) == xfer);
3677 auto const vault = env.le(
keylet);
3678 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
3679 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
3686 {.depositor = owner,
3688 .amount = asset(Number(1000 + (37 * 5), -1))}));
3691 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value());
3693 auto const vault = env.le(
keylet);
3694 BEAST_EXPECT(vault->at(sfAssetsAvailable) ==
beast::kZero);
3695 BEAST_EXPECT(vault->at(sfAssetsTotal) ==
beast::kZero);
3698 env(vault.del({.owner = owner, .id = keylet.key}));
3701 {.initialIOU =
Number(11875, -2)});
3704 Env
const env{*
this, testableAmendments()};
3706 env.current()->fees().accountReserve(0, 1).drops() /
kDropsPerXrp.drops(),
3707 env.current()->fees().increment.drops() /
kDropsPerXrp.drops()};
3720 testcase(
"IOU no trust line to depositor no reserve");
3721 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3727 env.trust(asset(0), owner);
3730 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
3734 BEAST_EXPECT(trustline ==
nullptr);
3736 env(ticket::create(owner, 1));
3740 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
3744 env(pay(charlie, owner, XRP(incReserve)));
3751 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});
3763 testcase(
"IOU no reserve for share MPToken");
3764 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3768 env(pay(owner, charlie, asset(100)));
3771 env(ticket::create(charlie, 3));
3775 tx = vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(100)});
3779 env(pay(issuer, charlie, XRP(incReserve)));
3786 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});
3796 Env env{*
this, testableAmendments()};
3797 Account const issuer{
"issuer"};
3799 Account const depositor{
"depositor"};
3800 Account const charlie{
"charlie"};
3801 Account const pdOwner{
"pdOwner"};
3802 Account const credIssuer1{
"credIssuer1"};
3803 Account const credIssuer2{
"credIssuer2"};
3805 Vault const vault{env};
3806 env.fund(XRP(1000), issuer, owner, depositor, charlie, pdOwner, credIssuer1, credIssuer2);
3808 env(fset(issuer, asfAllowTrustLineClawback));
3810 env.require(Flags(issuer, asfAllowTrustLineClawback));
3813 env.trust(asset(1000), owner);
3814 env(pay(issuer, owner, asset(500)));
3815 env.trust(asset(1000), depositor);
3816 env(pay(issuer, depositor, asset(500)));
3817 env.trust(asset(1000), charlie);
3818 env(pay(issuer, charlie, asset(5)));
3821 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
3824 BEAST_EXPECT(env.le(
keylet));
3827 testcase(
"private vault owner can deposit");
3828 auto tx = vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(50)});
3833 testcase(
"private vault depositor not authorized yet");
3835 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
3840 testcase(
"private vault cannot set non-existing domain");
3841 auto tx = vault.set({.owner = owner, .id =
keylet.key});
3847 testcase(
"private vault set domainId");
3850 pdomain::Credentials
const credentials1{
3851 {.issuer = credIssuer1, .credType = credType}};
3853 env(pdomain::setTx(pdOwner, credentials1));
3854 auto const domainId1 = [&]() {
3856 return pdomain::getNewDomain(env.meta());
3859 auto tx = vault.set({.owner = owner, .id =
keylet.key});
3871 {.issuer = credIssuer1, .credType = credType},
3872 {.issuer = credIssuer2, .credType = credType}};
3875 auto const domainId = [&]() {
3877 return pdomain::getNewDomain(env.meta());
3880 auto tx = vault.set({.owner = owner, .id =
keylet.key});
3886 tx = vault.set({.owner = owner, .id =
keylet.key});
3894 testcase(
"private vault depositor still not authorized");
3896 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
3901 auto const credKeylet = credentials::keylet(depositor, credIssuer1, credType);
3903 testcase(
"private vault depositor now authorized");
3904 env(credentials::create(depositor, credIssuer1, credType));
3905 env(credentials::accept(depositor, credIssuer1, credType));
3906 env(credentials::create(charlie, credIssuer1, credType));
3909 auto credSle = env.le(credKeylet);
3910 BEAST_EXPECT(credSle !=
nullptr);
3913 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
3917 tx = vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(50)});
3923 testcase(
"private vault depositor lost authorization");
3924 env(credentials::deleteCred(credIssuer1, depositor, credIssuer1, credType));
3925 env(credentials::deleteCred(credIssuer1, charlie, credIssuer1, credType));
3927 auto credSle = env.le(credKeylet);
3928 BEAST_EXPECT(credSle ==
nullptr);
3931 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
3937 auto const vault = env.le(
keylet);
3938 BEAST_EXPECT(vault !=
nullptr);
3939 return MPTIssue(vault->at(sfShareMPTID));
3943 testcase(
"private vault expired authorization");
3944 uint32_t
const closeTime =
3945 env.current()->header().parentCloseTime.time_since_epoch().count();
3947 auto tx0 = credentials::create(depositor, credIssuer2, credType);
3948 tx0[sfExpiration] = closeTime + 20;
3950 tx0 = credentials::create(charlie, credIssuer2, credType);
3951 tx0[sfExpiration] = closeTime + 20;
3955 env(credentials::accept(depositor, credIssuer2, credType));
3956 env(credentials::accept(charlie, credIssuer2, credType));
3962 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
3966 auto const tokenKeylet =
3968 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3977 auto const credsKeylet = credentials::keylet(depositor, credIssuer2, credType);
3978 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3981 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1)});
3985 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3989 auto const credsKeylet = credentials::keylet(charlie, credIssuer2, credType);
3990 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3991 auto const tokenKeylet =
3993 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3996 vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(2)});
4000 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
4001 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
4006 testcase(
"private vault reset domainId");
4007 auto tx = vault.set({.owner = owner, .id =
keylet.key});
4008 tx[sfDomainID] =
"0";
4012 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
4016 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
4020 tx = vault.clawback(
4021 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
4024 tx = vault.clawback(
4025 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(0)});
4042 testcase(
"private vault share transfer after depositor loses domain");
4058 Env env{*
this, testableAmendments()};
4059 Account const issuer{
"issuer"};
4061 Account const depositor{
"depositor"};
4063 Account const pdOwner{
"pdOwner"};
4064 Account const credIssuer{
"credIssuer"};
4066 Vault const vault{env};
4067 env.fund(XRP(1000), issuer, owner, depositor, bob, pdOwner, credIssuer);
4071 env.trust(asset(1000), owner);
4072 env(pay(issuer, owner, asset(500)));
4073 env.trust(asset(1000), depositor);
4074 env(pay(issuer, depositor, asset(500)));
4075 env.trust(asset(1000), bob);
4076 env(pay(issuer, bob, asset(500)));
4081 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
4085 pdomain::Credentials
const credentials{{.issuer = credIssuer, .credType = credType}};
4087 auto const domainId = [&]() {
4089 return pdomain::getNewDomain(env.meta());
4092 auto domainTx = vault.set({.owner = owner, .id =
keylet.key});
4093 domainTx[sfDomainID] =
to_string(domainId);
4100 env(credentials::create(depositor, credIssuer, credType));
4101 env(credentials::accept(depositor, credIssuer, credType));
4102 env(credentials::create(bob, credIssuer, credType));
4103 env(credentials::accept(bob, credIssuer, credType));
4106 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}));
4107 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(100)}));
4111 auto const sle = env.le(
keylet);
4112 BEAST_EXPECT(sle !=
nullptr);
4113 return MPTIssue(sle->at(sfShareMPTID));
4117 auto const credKeylet = credentials::keylet(depositor, credIssuer, credType);
4118 env(credentials::deleteCred(credIssuer, depositor, credIssuer, credType));
4120 BEAST_EXPECT(env.le(credKeylet) ==
nullptr);
4126 Account const charlie{
"charlie"};
4127 env.fund(XRP(1000), charlie);
4130 vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(1)});
4136 env(pay(depositor, bob, shares(1)), Ter{
tecNO_AUTH});
4144 BEAST_EXPECT(expectOffers(env, depositor, 0));
4368 auto testCase = [&,
this](
4370 Env env{*
this, testableAmendments()};
4372 Account const issuer{
"issuer"};
4373 Account const depositor{
"depositor"};
4375 env.fund(XRP(1000), issuer, owner, depositor);
4376 env(fset(issuer, asfAllowTrustLineClawback));
4380 env.trust(asset(1000), owner);
4381 env.trust(asset(1000), depositor);
4382 env(pay(issuer, owner, asset(200)));
4383 env(pay(issuer, depositor, asset(200)));
4386 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
4387 tx[sfScale] =
scale;
4390 auto const [vaultAccount, issuanceId] =
4392 auto const vault = env.le(
keylet);
4393 return {
Account(
"vault", vault->at(sfAccount)), vault->at(sfShareMPTID)};
4396 env.memoize(vaultAccount);
4399 return env.app().getOpenLedger().modify(
4403 if (!BEAST_EXPECT(vault))
4406 if (!BEAST_EXPECT(shares))
4408 if (fn(*vault, *shares))
4423 .depositor = depositor,
4424 .vaultAccount = vaultAccount,
4434 testCase(18, [&,
this](Env& env, Data
d) {
4435 testcase(
"Scale deposit overflow on first deposit");
4436 auto tx =
d.vault.deposit(
4437 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(10)});
4442 testCase(18, [&,
this](Env& env, Data
d) {
4443 testcase(
"Scale deposit overflow on second deposit");
4446 auto tx =
d.vault.deposit(
4447 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(5)});
4453 auto tx =
d.vault.deposit(
4454 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(10)});
4460 testCase(18, [&,
this](Env& env, Data
d) {
4461 testcase(
"Scale deposit overflow on total shares");
4464 auto tx =
d.vault.deposit(
4465 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(5)});
4471 auto tx =
d.vault.deposit(
4472 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(5)});
4478 testCase(1, [&,
this](Env& env, Data
d) {
4481 auto const start = env.balance(
d.depositor,
d.assets).number();
4482 auto tx =
d.vault.deposit(
4483 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(1)});
4486 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(10));
4487 BEAST_EXPECT(env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start - 1));
4490 testCase(1, [&,
this](Env& env, Data
d) {
4491 testcase(
"Scale deposit insignificant amount");
4493 auto tx =
d.vault.deposit(
4494 {.depositor =
d.depositor,
4500 testCase(1, [&,
this](Env& env, Data
d) {
4501 testcase(
"Scale deposit exact, using full precision");
4503 auto const start = env.balance(
d.depositor,
d.assets).number();
4504 auto tx =
d.vault.deposit(
4505 {.depositor =
d.depositor,
4510 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(15));
4512 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start -
Number(15, -1)));
4515 testCase(1, [&,
this](Env& env, Data
d) {
4516 testcase(
"Scale deposit exact, truncating from .5");
4518 auto const start = env.balance(
d.depositor,
d.assets).number();
4522 auto tx =
d.vault.deposit(
4523 {.depositor =
d.depositor,
4528 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(12));
4530 env.balance(
d.depositor,
d.assets) ==
4535 auto tx =
d.vault.deposit(
4536 {.depositor =
d.depositor,
4541 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(24));
4543 env.balance(
d.depositor,
d.assets) ==
4548 auto tx =
d.vault.deposit(
4549 {.depositor =
d.depositor,
4554 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(36));
4556 env.balance(
d.depositor,
d.assets) ==
4561 testCase(1, [&,
this](Env& env, Data
d) {
4562 testcase(
"Scale deposit exact, truncating from .01");
4564 auto const start = env.balance(
d.depositor,
d.assets).number();
4566 auto tx =
d.vault.deposit(
4567 {.depositor =
d.depositor,
4572 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(12));
4574 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start -
Number(12, -1)));
4578 auto tx =
d.vault.deposit(
4579 {.depositor =
d.depositor,
4584 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(18));
4586 env.balance(
d.depositor,
d.assets) ==
4591 testCase(1, [&,
this](Env& env, Data
d) {
4592 testcase(
"Scale deposit exact, truncating from .99");
4594 auto const start = env.balance(
d.depositor,
d.assets).number();
4596 auto tx =
d.vault.deposit(
4597 {.depositor =
d.depositor,
4602 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(12));
4604 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start -
Number(12, -1)));
4608 auto tx =
d.vault.deposit(
4609 {.depositor =
d.depositor,
4614 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(18));
4616 env.balance(
d.depositor,
d.assets) ==
4621 testCase(1, [&,
this](Env& env, Data
d) {
4623 auto const start = env.balance(
d.depositor,
d.assets).number();
4624 auto tx =
d.vault.deposit(
4625 {.depositor =
d.depositor,
4630 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(1000));
4632 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start -
Number(100, 0)));
4644 auto const start = env.balance(
d.depositor,
d.assets).number();
4645 auto tx =
d.vault.withdraw(
4646 {.depositor =
d.depositor,
4651 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(900));
4653 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start +
Number(10, 0)));
4661 testcase(
"Scale redeem with rounding");
4666 auto const start = env.balance(
d.depositor,
d.assets).number();
4667 d.peek([](
SLE& vault,
auto&) ->
bool {
4668 vault[sfAssetsAvailable] =
Number(1);
4676 auto tx =
d.vault.withdraw(
4677 {.depositor =
d.depositor,
4682 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(900 - 25));
4684 env.balance(
d.depositor,
d.assets) ==
4687 env.balance(
d.vaultAccount,
d.assets) ==
4690 env.balance(
d.vaultAccount,
d.shares) ==
4700 auto const start = env.balance(
d.depositor,
d.assets).number();
4702 tx =
d.vault.withdraw(
4703 {.depositor =
d.depositor,
4708 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(875 - 21));
4710 env.balance(
d.depositor,
d.assets) ==
4713 env.balance(
d.vaultAccount,
d.assets) ==
4716 env.balance(
d.vaultAccount,
d.shares) ==
4722 auto const rest = env.balance(
d.depositor,
d.shares).number();
4724 tx =
d.vault.withdraw(
4725 {.depositor =
d.depositor,
4730 BEAST_EXPECT(env.balance(
d.depositor,
d.shares).number() == 0);
4731 BEAST_EXPECT(env.balance(
d.vaultAccount,
d.assets).number() == 0);
4732 BEAST_EXPECT(env.balance(
d.vaultAccount,
d.shares).number() == 0);
4736 testCase(18, [&,
this](Env& env, Data
d) {
4737 testcase(
"Scale withdraw overflow");
4740 auto tx =
d.vault.deposit(
4741 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(5)});
4747 auto tx =
d.vault.withdraw(
4748 {.depositor =
d.depositor,
4756 testCase(1, [&,
this](Env& env, Data
d) {
4758 auto const start = env.balance(
d.depositor,
d.assets).number();
4759 auto tx =
d.vault.deposit(
4760 {.depositor =
d.depositor,
4765 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(1000));
4767 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start -
Number(100, 0)));
4782 auto const start = env.balance(
d.depositor,
d.assets).number();
4783 auto tx =
d.vault.withdraw(
4784 {.depositor =
d.depositor,
4789 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(900));
4791 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start +
Number(10, 0)));
4799 testcase(
"Scale withdraw insignificant amount");
4800 auto tx =
d.vault.withdraw(
4801 {.depositor =
d.depositor,
4808 testcase(
"Scale withdraw with rounding assets");
4816 auto const start = env.balance(
d.depositor,
d.assets).number();
4817 d.peek([](
SLE& vault,
auto&) ->
bool {
4818 vault[sfAssetsAvailable] =
Number(1);
4826 auto tx =
d.vault.withdraw(
4827 {.depositor =
d.depositor,
4832 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(900 - 25));
4834 env.balance(
d.depositor,
d.assets) ==
4837 env.balance(
d.vaultAccount,
d.assets) ==
4840 env.balance(
d.vaultAccount,
d.shares) ==
4845 testcase(
"Scale withdraw with rounding shares up");
4853 auto const start = env.balance(
d.depositor,
d.assets).number();
4854 auto tx =
d.vault.withdraw(
4855 {.depositor =
d.depositor,
4860 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(875 - 38));
4862 env.balance(
d.depositor,
d.assets) ==
4865 env.balance(
d.vaultAccount,
d.assets) ==
4868 env.balance(
d.vaultAccount,
d.shares) ==
4873 testcase(
"Scale withdraw with rounding shares down");
4881 auto const start = env.balance(
d.depositor,
d.assets).number();
4882 auto tx =
d.vault.withdraw(
4883 {.depositor =
d.depositor,
4888 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(837 - 37));
4890 env.balance(
d.depositor,
d.assets) ==
4893 env.balance(
d.vaultAccount,
d.assets) ==
4896 env.balance(
d.vaultAccount,
d.shares) ==
4901 testcase(
"Scale withdraw tiny amount");
4903 auto const start = env.balance(
d.depositor,
d.assets).number();
4904 auto tx =
d.vault.withdraw(
4905 {.depositor =
d.depositor,
4910 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(800 - 1));
4912 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start +
Number(1, -1)));
4914 env.balance(
d.vaultAccount,
d.assets) ==
4917 env.balance(
d.vaultAccount,
d.shares) ==
4923 auto const rest = env.balance(
d.vaultAccount,
d.assets).number();
4925 tx =
d.vault.withdraw(
4926 {.depositor =
d.depositor,
4931 BEAST_EXPECT(env.balance(
d.depositor,
d.shares).number() == 0);
4932 BEAST_EXPECT(env.balance(
d.vaultAccount,
d.assets).number() == 0);
4933 BEAST_EXPECT(env.balance(
d.vaultAccount,
d.shares).number() == 0);
4937 testCase(18, [&,
this](Env& env, Data
d) {
4938 testcase(
"Scale clawback overflow");
4941 auto tx =
d.vault.deposit(
4942 {.depositor =
d.depositor, .id =
d.keylet.key, .amount =
d.asset(5)});
4948 auto tx =
d.vault.clawback(
4949 {.issuer =
d.issuer,
4951 .holder =
d.depositor,
4958 testCase(1, [&,
this](Env& env, Data
d) {
4960 auto const start = env.balance(
d.depositor,
d.assets).number();
4961 auto tx =
d.vault.deposit(
4962 {.depositor =
d.depositor,
4967 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(1000));
4969 env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start -
Number(100, 0)));
4983 auto const start = env.balance(
d.depositor,
d.assets).number();
4984 auto tx =
d.vault.clawback(
4985 {.issuer =
d.issuer,
4987 .holder =
d.depositor,
4991 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(900));
4992 BEAST_EXPECT(env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start));
5000 testcase(
"Scale clawback insignificant amount");
5001 auto tx =
d.vault.clawback(
5002 {.issuer =
d.issuer,
5004 .holder =
d.depositor,
5010 testcase(
"Scale clawback with rounding assets");
5018 auto const start = env.balance(
d.depositor,
d.assets).number();
5019 auto tx =
d.vault.clawback(
5020 {.issuer =
d.issuer,
5022 .holder =
d.depositor,
5026 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(900 - 25));
5027 BEAST_EXPECT(env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start));
5029 env.balance(
d.vaultAccount,
d.assets) ==
5032 env.balance(
d.vaultAccount,
d.shares) ==
5037 testcase(
"Scale clawback with rounding shares up");
5045 auto const start = env.balance(
d.depositor,
d.assets).number();
5046 auto tx =
d.vault.clawback(
5047 {.issuer =
d.issuer,
5049 .holder =
d.depositor,
5053 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(875 - 38));
5054 BEAST_EXPECT(env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start));
5056 env.balance(
d.vaultAccount,
d.assets) ==
5059 env.balance(
d.vaultAccount,
d.shares) ==
5064 testcase(
"Scale clawback with rounding shares down");
5072 auto const start = env.balance(
d.depositor,
d.assets).number();
5073 auto tx =
d.vault.clawback(
5074 {.issuer =
d.issuer,
5076 .holder =
d.depositor,
5080 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(837 - 37));
5081 BEAST_EXPECT(env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start));
5083 env.balance(
d.vaultAccount,
d.assets) ==
5086 env.balance(
d.vaultAccount,
d.shares) ==
5091 testcase(
"Scale clawback tiny amount");
5093 auto const start = env.balance(
d.depositor,
d.assets).number();
5094 auto tx =
d.vault.clawback(
5095 {.issuer =
d.issuer,
5097 .holder =
d.depositor,
5101 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(800 - 1));
5102 BEAST_EXPECT(env.balance(
d.depositor,
d.assets) ==
STAmount(
d.asset, start));
5104 env.balance(
d.vaultAccount,
d.assets) ==
5107 env.balance(
d.vaultAccount,
d.shares) ==
5113 auto const rest = env.balance(
d.vaultAccount,
d.assets).number();
5114 d.peek([](
SLE& vault,
auto&) ->
bool {
5115 vault[sfAssetsAvailable] =
Number(5);
5123 tx =
d.vault.clawback(
5124 {.issuer =
d.issuer,
5126 .holder =
d.depositor,
5130 BEAST_EXPECT(env.balance(
d.depositor,
d.shares).number() == 0);
5131 BEAST_EXPECT(env.balance(
d.vaultAccount,
d.assets).number() == 0);
5132 BEAST_EXPECT(env.balance(
d.vaultAccount,
d.shares).number() == 0);
5139 testCase(1, [&,
this](Env& env, Data
d) {
5140 using namespace loan_broker;
5141 using namespace loan;
5143 testcase(
"Scale clawback clamped with outstanding loan");
5145 auto tx =
d.vault.deposit(
5146 {.depositor =
d.depositor,
5151 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(1000));
5154 auto const brokerKeylet =
5156 env(
set(
d.owner,
d.keylet.key));
5163 kPaymentInterval(120),
5165 Sig(sfCounterpartySignature,
d.owner),
5166 Fee(env.current()->fees().base * 2),
5171 auto const sle = env.le(
d.keylet);
5172 BEAST_EXPECT(sle->at(sfAssetsAvailable) ==
STAmount(
d.asset,
Number(60, 0)));
5173 BEAST_EXPECT(sle->at(sfAssetsTotal) ==
STAmount(
d.asset,
Number(100, 0)));
5178 tx =
d.vault.clawback(
5179 {.issuer =
d.issuer,
5181 .holder =
d.depositor,
5187 auto const sle = env.le(
d.keylet);
5188 BEAST_EXPECT(sle !=
nullptr);
5189 BEAST_EXPECT(sle->at(sfAssetsAvailable) ==
STAmount(
d.asset,
Number(0, 0)));
5190 BEAST_EXPECT(sle->at(sfAssetsTotal) ==
STAmount(
d.asset,
Number(40, 0)));
5193 BEAST_EXPECT(env.balance(
d.depositor,
d.shares) ==
d.share(400));
5204 Env env{*
this, testableAmendments()};
5206 Account const issuer{
"issuer"};
5207 Vault const vault{env};
5208 env.fund(XRP(1000), issuer, owner);
5212 env.trust(asset(1000), owner);
5213 env(pay(issuer, owner, asset(200)));
5216 auto const sequence = env.seq(owner);
5217 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
5223 auto tx1 = vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(50)});
5226 auto tx2 = vault.set({.owner = owner, .id =
keylet.key});
5227 tx2[sfAssetsMaximum] = asset(1000).number();
5232 auto const sleVault = [&env,
keylet =
keylet,
this]() {
5233 auto const vault = env.le(
keylet);
5234 BEAST_EXPECT(vault !=
nullptr);
5238 auto const check = [&,
keylet =
keylet, sle = sleVault,
this](
5241 BEAST_EXPECT(vault.isObject());
5243 static constexpr auto kCheckString =
5245 return node.isMember(field.fieldName) && node[field.fieldName].isString() &&
5246 node[field.fieldName] == v;
5248 static constexpr auto kCheckObject =
5250 return node.isMember(field.fieldName) && node[field.fieldName].isObject() &&
5251 node[field.fieldName] == v;
5253 static constexpr auto kCheckInt = [](
auto& node,
SField const& field,
int v) ->
bool {
5254 return node.isMember(field.fieldName) &&
5255 ((node[field.fieldName].isInt() && node[field.fieldName] ==
json::Int(v)) ||
5256 (node[field.fieldName].isUInt() && node[field.fieldName] ==
json::UInt(v)));
5259 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
5260 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(
keylet.key));
5261 BEAST_EXPECT(kCheckInt(vault, sfFlags, 0));
5264 BEAST_EXPECT(kCheckString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
5265 BEAST_EXPECT(kCheckObject(vault, sfAsset,
toJson(sle->at(sfAsset))));
5266 BEAST_EXPECT(kCheckString(vault, sfAssetsAvailable,
"50"));
5267 BEAST_EXPECT(kCheckString(vault, sfAssetsMaximum,
"1000"));
5268 BEAST_EXPECT(kCheckString(vault, sfAssetsTotal,
"50"));
5269 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
5271 auto const strShareID =
strHex(sle->at(sfShareMPTID));
5272 BEAST_EXPECT(kCheckString(vault, sfShareMPTID, strShareID));
5273 BEAST_EXPECT(kCheckString(vault, sfOwner,
toBase58(owner.id())));
5274 BEAST_EXPECT(kCheckInt(vault, sfSequence, sequence));
5277 if (issuance.isObject())
5279 BEAST_EXPECT(issuance[
"LedgerEntryType"].asString() ==
"MPTokenIssuance");
5280 BEAST_EXPECT(issuance[jss::mpt_issuance_id].asString() == strShareID);
5281 BEAST_EXPECT(kCheckInt(issuance, sfSequence, 1));
5282 BEAST_EXPECT(kCheckInt(
5283 issuance, sfFlags,
int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer)));
5284 BEAST_EXPECT(kCheckString(issuance, sfOutstandingAmount,
"50000000"));
5289 testcase(
"RPC ledger_entry selected by key");
5291 jvParams[jss::ledger_index] = jss::validated;
5293 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5295 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
5296 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
5297 check(jvVault[jss::result][jss::node]);
5301 testcase(
"RPC ledger_entry selected by owner and seq");
5303 jvParams[jss::ledger_index] = jss::validated;
5304 jvParams[jss::vault][jss::owner] = owner.human();
5305 jvParams[jss::vault][jss::seq] = sequence;
5306 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5308 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
5309 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
5310 check(jvVault[jss::result][jss::node]);
5314 testcase(
"RPC ledger_entry cannot find vault by key");
5316 jvParams[jss::ledger_index] = jss::validated;
5318 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5319 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
5323 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
5325 jvParams[jss::ledger_index] = jss::validated;
5326 jvParams[jss::vault][jss::owner] = issuer.human();
5327 jvParams[jss::vault][jss::seq] = 1'000'000;
5328 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5329 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
5333 testcase(
"RPC ledger_entry malformed key");
5335 jvParams[jss::ledger_index] = jss::validated;
5336 jvParams[jss::vault] = 42;
5337 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5338 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
5342 testcase(
"RPC ledger_entry malformed owner");
5344 jvParams[jss::ledger_index] = jss::validated;
5345 jvParams[jss::vault][jss::owner] = 42;
5346 jvParams[jss::vault][jss::seq] = sequence;
5347 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5348 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedOwner");
5352 testcase(
"RPC ledger_entry malformed seq");
5354 jvParams[jss::ledger_index] = jss::validated;
5355 jvParams[jss::vault][jss::owner] = issuer.human();
5356 jvParams[jss::vault][jss::seq] =
"foo";
5357 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5358 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
5362 testcase(
"RPC ledger_entry negative seq");
5364 jvParams[jss::ledger_index] = jss::validated;
5365 jvParams[jss::vault][jss::owner] = issuer.human();
5366 jvParams[jss::vault][jss::seq] = -1;
5367 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5368 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
5372 testcase(
"RPC ledger_entry oversized seq");
5374 jvParams[jss::ledger_index] = jss::validated;
5375 jvParams[jss::vault][jss::owner] = issuer.human();
5376 jvParams[jss::vault][jss::seq] = 1e20;
5377 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5378 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
5382 testcase(
"RPC ledger_entry bool seq");
5384 jvParams[jss::ledger_index] = jss::validated;
5385 jvParams[jss::vault][jss::owner] = issuer.human();
5386 jvParams[jss::vault][jss::seq] =
true;
5387 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
5388 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
5395 jvParams[jss::account] = owner.human();
5396 jvParams[jss::type] = jss::vault;
5397 auto jv = env.rpc(
"json",
"account_objects",
to_string(jvParams))[jss::result];
5399 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
5400 check(jv[jss::account_objects][0u]);
5407 jvParams[jss::ledger_index] = jss::validated;
5408 jvParams[jss::binary] =
false;
5409 jvParams[jss::type] = jss::vault;
5411 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
5412 check(jv[jss::result][jss::state][0u]);
5416 testcase(
"RPC vault_info command line");
5419 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
5420 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
5421 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
5427 jvParams[jss::ledger_index] = jss::validated;
5429 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5431 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
5432 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
5433 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
5437 testcase(
"RPC vault_info invalid vault_id");
5439 jvParams[jss::ledger_index] = jss::validated;
5440 jvParams[jss::vault_id] =
"foobar";
5441 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5442 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5446 testcase(
"RPC vault_info json invalid index");
5448 jvParams[jss::ledger_index] = jss::validated;
5449 jvParams[jss::vault_id] = 0;
5450 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5451 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5455 testcase(
"RPC vault_info json by owner and sequence");
5457 jvParams[jss::ledger_index] = jss::validated;
5458 jvParams[jss::owner] = owner.human();
5459 jvParams[jss::seq] = sequence;
5460 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5462 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
5463 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
5464 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
5468 testcase(
"RPC vault_info json malformed sequence");
5470 jvParams[jss::ledger_index] = jss::validated;
5471 jvParams[jss::owner] = owner.human();
5472 jvParams[jss::seq] =
"foobar";
5473 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5474 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5478 testcase(
"RPC vault_info json invalid sequence");
5480 jvParams[jss::ledger_index] = jss::validated;
5481 jvParams[jss::owner] = owner.human();
5482 jvParams[jss::seq] = 0;
5483 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5484 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5488 testcase(
"RPC vault_info json negative sequence");
5490 jvParams[jss::ledger_index] = jss::validated;
5491 jvParams[jss::owner] = owner.human();
5492 jvParams[jss::seq] = -1;
5493 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5494 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5498 testcase(
"RPC vault_info json oversized sequence");
5500 jvParams[jss::ledger_index] = jss::validated;
5501 jvParams[jss::owner] = owner.human();
5502 jvParams[jss::seq] = 1e20;
5503 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5504 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5508 testcase(
"RPC vault_info json bool sequence");
5510 jvParams[jss::ledger_index] = jss::validated;
5511 jvParams[jss::owner] = owner.human();
5512 jvParams[jss::seq] =
true;
5513 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5514 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5518 testcase(
"RPC vault_info json malformed owner");
5520 jvParams[jss::ledger_index] = jss::validated;
5521 jvParams[jss::owner] =
"foobar";
5522 jvParams[jss::seq] = sequence;
5523 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5524 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5528 testcase(
"RPC vault_info json invalid combination only owner");
5530 jvParams[jss::ledger_index] = jss::validated;
5531 jvParams[jss::owner] = owner.human();
5532 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5533 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5537 testcase(
"RPC vault_info json invalid combination only seq");
5539 jvParams[jss::ledger_index] = jss::validated;
5540 jvParams[jss::seq] = sequence;
5541 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5542 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5546 testcase(
"RPC vault_info json invalid combination seq vault_id");
5548 jvParams[jss::ledger_index] = jss::validated;
5550 jvParams[jss::seq] = sequence;
5551 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5552 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5556 testcase(
"RPC vault_info json invalid combination owner vault_id");
5558 jvParams[jss::ledger_index] = jss::validated;
5560 jvParams[jss::owner] = owner.human();
5561 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5562 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5567 "RPC vault_info json invalid combination owner seq "
5570 jvParams[jss::ledger_index] = jss::validated;
5572 jvParams[jss::seq] = sequence;
5573 jvParams[jss::owner] = owner.human();
5574 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5575 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5579 testcase(
"RPC vault_info json no input");
5581 jvParams[jss::ledger_index] = jss::validated;
5582 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5583 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5587 testcase(
"RPC vault_info command line invalid index");
5588 json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
5589 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
5593 testcase(
"RPC vault_info command line invalid index");
5594 json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
5595 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
5599 testcase(
"RPC vault_info command line invalid index");
5601 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"entryNotFound");
5605 testcase(
"RPC vault_info command line invalid ledger");
5607 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"lgrNotFound");
5699 using namespace loan_broker;
5700 using namespace loan;
5703 auto const vaultAssetBalance = [&](
Keylet const& vaultKeylet) {
5704 auto const sleVault = env.le(vaultKeylet);
5705 BEAST_EXPECT(sleVault !=
nullptr);
5707 return std::make_pair(sleVault->at(sfAssetsAvailable), sleVault->at(sfAssetsTotal));
5710 auto const vaultShareBalance = [&](
Keylet const& vaultKeylet) {
5711 auto const sleVault = env.le(vaultKeylet);
5712 BEAST_EXPECT(sleVault !=
nullptr);
5715 BEAST_EXPECT(sleIssuance !=
nullptr);
5717 return sleIssuance->at(sfOutstandingAmount);
5720 auto const setupVault = [&](
PrettyAsset const& asset,
5723 Vault const vault{env};
5725 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
5729 auto const& vaultSle = env.le(vaultKeylet);
5730 BEAST_EXPECT(vaultSle !=
nullptr);
5732 Asset const share = vaultSle->at(sfShareMPTID);
5735 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
5739 auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet);
5740 BEAST_EXPECT(availablePreDefault == totalPreDefault);
5741 BEAST_EXPECT(availablePreDefault == asset(100).value());
5746 .id = vaultKeylet.key,
5747 .holder = depositor,
5748 .amount = share(0).value()}),
5752 auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
5753 auto const& brokerKeylet =
5756 env(
set(owner, vaultKeylet.key));
5762 env(
set(depositor, brokerKeylet.key, asset(100).value()),
5765 kPaymentInterval(120),
5767 Sig(sfCounterpartySignature, owner),
5768 Fee(env.current()->fees().base * 2),
5776 .id = vaultKeylet.key,
5777 .holder = depositor,
5778 .amount = share(0).value()}),
5784 env(manage(owner, loanKeylet.key, tfLoanDefault), Ter(
tesSUCCESS));
5786 auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet);
5788 BEAST_EXPECT(availablePostDefault == totalPostDefault);
5789 BEAST_EXPECT(availablePostDefault == asset(0).value());
5790 BEAST_EXPECT(vaultShareBalance(vaultKeylet) == sharesAvailable);
5795 auto const testCase = [&](
PrettyAsset const& asset,
5800 testcase(
"VaultClawback (share) - " + prefix +
" owner asset clawback fails");
5801 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5805 auto const expectedTer = [&]() {
5812 env(vault.clawback({
5814 .id = vaultKeylet.key,
5815 .holder = depositor,
5816 .amount = asset(100).value(),
5824 "VaultClawback (share) - " + prefix +
" owner incomplete share clawback fails");
5825 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5826 auto const& vaultSle = env.le(vaultKeylet);
5827 if (!BEAST_EXPECT(vaultSle))
5829 Asset const share = vaultSle->at(sfShareMPTID);
5830 env(vault.clawback({
5832 .id = vaultKeylet.key,
5833 .holder = depositor,
5834 .amount = share(1).value(),
5842 "VaultClawback (share) - " + prefix +
5843 " owner implicit complete share clawback");
5844 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5845 env(vault.clawback({
5847 .id = vaultKeylet.key,
5848 .holder = depositor,
5858 "VaultClawback (share) - " + prefix +
5859 " owner explicit complete share clawback succeeds");
5860 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
5861 auto const& vaultSle = env.le(vaultKeylet);
5862 if (!BEAST_EXPECT(vaultSle))
5864 Asset const share = vaultSle->at(sfShareMPTID);
5865 env(vault.clawback({
5867 .id = vaultKeylet.key,
5868 .holder = depositor,
5869 .amount = share(vaultShareBalance(vaultKeylet)).value(),
5875 testcase(
"VaultClawback (share) - " + prefix +
" owner can clawback own shares");
5876 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
5877 auto const& vaultSle = env.le(vaultKeylet);
5878 if (!BEAST_EXPECT(vaultSle))
5880 Asset const share = vaultSle->at(sfShareMPTID);
5881 env(vault.clawback({
5883 .id = vaultKeylet.key,
5885 .amount = share(vaultShareBalance(vaultKeylet)).value(),
5892 testcase(
"VaultClawback (share) - " + prefix +
" empty vault share clawback fails");
5893 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
5894 auto const& vaultSle = env.le(vaultKeylet);
5895 if (!BEAST_EXPECT(vaultSle))
5897 Asset const share = vaultSle->at(sfShareMPTID);
5898 env(vault.clawback({
5900 .id = vaultKeylet.key,
5902 .amount = share(vaultShareBalance(vaultKeylet)).value(),
5907 env(vault.clawback({
5909 .id = vaultKeylet.key,
5911 .amount = share(vaultShareBalance(vaultKeylet)).value(),
5919 Account const depositor{
"bob"};
5920 Account const issuer{
"issuer"};
5922 env.fund(XRP(10000), issuer, owner, depositor);
5927 testCase(xrp,
"XRP", owner, depositor);
5928 testCase(xrp,
"XRP (depositor is owner)", owner, owner);
5932 env(fset(issuer, asfAllowTrustLineClawback));
5935 env.trust(iou(1000), owner);
5936 env.trust(iou(1000), depositor);
5937 env(pay(issuer, owner, iou(100)));
5938 env(pay(issuer, depositor, iou(100)));
5940 testCase(iou,
"IOU", owner, depositor);
5941 testCase(iou,
"IOU (owner is issuer)", issuer, depositor);
5944 MPTTester mptt{env, issuer, kMptInitNoFund};
5945 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
5947 mptt.authorize({.account = owner});
5948 mptt.authorize({.account = depositor});
5949 env(pay(issuer, owner, mpt(1000)));
5950 env(pay(issuer, depositor, mpt(1000)));
5952 testCase(mpt,
"MPT", owner, depositor);
5953 testCase(mpt,
"MPT (owner is issuer)", issuer, depositor);
5960 using namespace loan_broker;
5961 using namespace loan;
5963 env.enableFeature(fixCleanup3_1_3);
5965 auto const setupVault = [&](
PrettyAsset const& asset,
5969 Vault const vault{env};
5971 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
5975 auto const& vaultSle = env.le(vaultKeylet);
5976 BEAST_EXPECT(vaultSle !=
nullptr);
5977 env.memoize(
Account(
"vault", vaultSle->at(sfAccount)));
5979 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
5986 auto const testCase = [&](
PrettyAsset const& asset,
5993 testcase(
"VaultClawback (asset) - " + prefix +
" issuer XRP clawback fails");
5994 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
5997 env(vault.clawback({
5999 .id = vaultKeylet.key,
6001 .amount = asset(1).value(),
6005 env(vault.clawback({
6007 .id = vaultKeylet.key,
6016 "VaultClawback (asset) - " + prefix +
" clawback for different asset fails");
6017 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6019 Account const issuer2{
"issuer2"};
6021 env(vault.clawback({
6023 .id = vaultKeylet.key,
6024 .holder = depositor,
6025 .amount = asset2(1).value(),
6032 "VaultClawback (asset) - " + prefix +
6033 " ambiguous owner/issuer asset clawback fails");
6034 auto [vault, vaultKeylet] = setupVault(asset, issuer, depositor, issuer);
6035 env(vault.clawback({
6037 .id = vaultKeylet.key,
6044 testcase(
"VaultClawback (asset) - " + prefix +
" non-issuer asset clawback fails");
6045 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6047 env(vault.clawback({
6049 .id = vaultKeylet.key,
6050 .holder = depositor,
6054 env(vault.clawback({
6056 .id = vaultKeylet.key,
6057 .holder = depositor,
6058 .amount = asset(1).value(),
6064 testcase(
"VaultClawback (asset) - " + prefix +
" issuer clawback from self fails");
6065 auto [vault, vaultKeylet] = setupVault(asset, owner, issuer, issuer);
6066 env(vault.clawback({
6068 .id = vaultKeylet.key,
6075 testcase(
"VaultClawback (asset) - " + prefix +
" issuer share clawback fails");
6076 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6077 auto const& vaultSle = env.le(vaultKeylet);
6078 if (!BEAST_EXPECT(vaultSle))
6080 Asset const share = vaultSle->at(sfShareMPTID);
6082 env(vault.clawback({
6084 .id = vaultKeylet.key,
6085 .holder = depositor,
6086 .amount = share(1).value(),
6093 "VaultClawback (asset) - " + prefix +
6094 " partial issuer asset clawback succeeds");
6095 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6097 env(vault.clawback({
6099 .id = vaultKeylet.key,
6100 .holder = depositor,
6101 .amount = asset(1).value(),
6108 "VaultClawback (asset) - " + prefix +
" full issuer asset clawback succeeds");
6109 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6111 env(vault.clawback({
6113 .id = vaultKeylet.key,
6114 .holder = depositor,
6115 .amount = asset(100).value(),
6122 "VaultClawback (asset) - " + prefix +
6123 " implicit full issuer asset clawback succeeds");
6124 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6126 env(vault.clawback({
6128 .id = vaultKeylet.key,
6129 .holder = depositor,
6136 "VaultClawback (asset) - " + prefix +
6137 " zero-amount clawback clamped with outstanding loan");
6138 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6140 auto const vaultSle = env.le(vaultKeylet);
6141 if (!BEAST_EXPECT(vaultSle))
6147 auto const brokerKeylet =
6149 env(
set(owner, vaultKeylet.key));
6154 env(
set(depositor, brokerKeylet.key, asset(40).value()),
6157 kPaymentInterval(120),
6159 Sig(sfCounterpartySignature, owner),
6160 Fee(env.current()->fees().base * 2),
6165 auto const sle = env.le(vaultKeylet);
6166 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
6167 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
6173 env(vault.clawback({
6175 .id = vaultKeylet.key,
6176 .holder = depositor,
6183 auto const sle = env.le(vaultKeylet);
6184 BEAST_EXPECT(sle !=
nullptr);
6185 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
6186 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
6189 auto const sharesAfter = env.balance(depositor, shares);
6190 BEAST_EXPECT(sharesAfter == shares(
Number{4, sle->at(sfScale) + 1}));
6196 "VaultClawback (asset) - " + prefix +
6197 " non-zero clawback clamped with outstanding loan");
6198 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6200 auto const vaultSle = env.le(vaultKeylet);
6201 if (!BEAST_EXPECT(vaultSle))
6206 auto const brokerKeylet =
6208 env(
set(owner, vaultKeylet.key));
6212 env(
set(depositor, brokerKeylet.key, asset(40).value()),
6215 kPaymentInterval(120),
6217 Sig(sfCounterpartySignature, owner),
6218 Fee(env.current()->fees().base * 2),
6223 auto const sle = env.le(vaultKeylet);
6224 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
6225 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
6229 env(vault.clawback({
6231 .id = vaultKeylet.key,
6232 .holder = depositor,
6233 .amount = asset(100).value(),
6239 auto const sle = env.le(vaultKeylet);
6240 BEAST_EXPECT(sle !=
nullptr);
6241 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
6242 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
6245 auto const sharesAfter = env.balance(depositor, shares);
6246 BEAST_EXPECT(sharesAfter == shares(
Number{4, sle->at(sfScale) + 1}));
6252 "VaultClawback (asset) - " + prefix +
6253 " partial clawback below available with outstanding loan");
6254 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6256 auto const vaultSle = env.le(vaultKeylet);
6257 if (!BEAST_EXPECT(vaultSle))
6262 auto const brokerKeylet =
6264 env(
set(owner, vaultKeylet.key));
6268 env(
set(depositor, brokerKeylet.key, asset(40).value()),
6271 kPaymentInterval(120),
6273 Sig(sfCounterpartySignature, owner),
6274 Fee(env.current()->fees().base * 2),
6279 auto const sle = env.le(vaultKeylet);
6280 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
6281 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
6285 env(vault.clawback({
6287 .id = vaultKeylet.key,
6288 .holder = depositor,
6289 .amount = asset(30).value(),
6295 auto const sle = env.le(vaultKeylet);
6296 BEAST_EXPECT(sle !=
nullptr);
6297 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(30).value());
6298 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(70).value());
6301 auto const sharesAfter = env.balance(depositor, shares);
6302 BEAST_EXPECT(sharesAfter == shares(
Number{7, sle->at(sfScale) + 1}));
6308 "VaultClawback (asset) - " + prefix +
6309 " clawback exactly equal to available with outstanding loan");
6310 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6312 auto const vaultSle = env.le(vaultKeylet);
6313 if (!BEAST_EXPECT(vaultSle))
6317 auto const brokerKeylet =
6319 env(
set(owner, vaultKeylet.key));
6323 env(
set(depositor, brokerKeylet.key, asset(40).value()),
6326 kPaymentInterval(120),
6328 Sig(sfCounterpartySignature, owner),
6329 Fee(env.current()->fees().base * 2),
6334 env(vault.clawback({
6336 .id = vaultKeylet.key,
6337 .holder = depositor,
6338 .amount = asset(60).value(),
6344 auto const sle = env.le(vaultKeylet);
6345 BEAST_EXPECT(sle !=
nullptr);
6346 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
6347 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
6350 auto const sharesAfter = env.balance(depositor, shares);
6351 BEAST_EXPECT(sharesAfter == shares(
Number{4, sle->at(sfScale) + 1}));
6357 "VaultClawback (asset) - " + prefix +
6358 " clawback with zero available (fully borrowed)");
6359 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
6361 auto const vaultSle = env.le(vaultKeylet);
6362 if (!BEAST_EXPECT(vaultSle))
6366 auto const brokerKeylet =
6368 env(
set(owner, vaultKeylet.key));
6372 env(
set(depositor, brokerKeylet.key, asset(100).value()),
6375 kPaymentInterval(120),
6377 Sig(sfCounterpartySignature, owner),
6378 Fee(env.current()->fees().base * 2),
6383 auto const sle = env.le(vaultKeylet);
6384 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
6385 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
6388 auto const sharesBefore = env.balance(depositor, shares);
6392 env(vault.clawback({
6394 .id = vaultKeylet.key,
6395 .holder = depositor,
6401 env(vault.clawback({
6403 .id = vaultKeylet.key,
6404 .holder = depositor,
6405 .amount = asset(50).value(),
6412 auto const sle = env.le(vaultKeylet);
6413 BEAST_EXPECT(sle !=
nullptr);
6414 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
6415 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
6416 auto const sharesAfter = env.balance(depositor, shares);
6417 BEAST_EXPECT(sharesAfter == sharesBefore);
6423 Account const depositor{
"bob"};
6424 Account const issuer{
"issuer"};
6426 env.fund(XRP(10000), issuer, owner, depositor);
6431 testCase(xrp,
"XRP", owner, depositor, issuer);
6435 env(fset(issuer, asfAllowTrustLineClawback));
6437 env.trust(iou(2000), owner);
6438 env.trust(iou(2000), depositor);
6439 env(pay(issuer, owner, iou(2000)));
6440 env(pay(issuer, depositor, iou(2000)));
6442 testCase(iou,
"IOU", owner, depositor, issuer);
6445 MPTTester mptt{env, issuer, kMptInitNoFund};
6446 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
6449 mptt.authorize({.account = owner});
6450 mptt.authorize({.account = depositor});
6451 env(pay(issuer, depositor, mpt(2000)));
6453 testCase(mpt,
"MPT", owner, depositor, issuer);
6459 "VaultClawback (asset) - IOU pre-fixCleanup3_1_3"
6460 " zero-amount clawback unclamped with outstanding loan");
6462 env.disableFeature(fixCleanup3_1_3);
6464 auto [vault, vaultKeylet] = setupVault(iou, owner, depositor, issuer);
6466 auto const vaultSle = env.le(vaultKeylet);
6467 BEAST_EXPECT(vaultSle !=
nullptr);
6474 auto const brokerKeylet =
6476 env(
set(owner, vaultKeylet.key));
6481 env(
set(depositor, brokerKeylet.key, iou(40).value()),
6484 kPaymentInterval(120),
6486 Sig(sfCounterpartySignature, owner),
6487 Fee(env.current()->fees().base * 2),
6492 auto const sle = env.le(vaultKeylet);
6493 BEAST_EXPECT(sle->at(sfAssetsAvailable) == iou(60).value());
6494 BEAST_EXPECT(sle->at(sfAssetsTotal) == iou(100).value());
6497 auto const sharesBefore = env.balance(depositor, shares);
6503 env(vault.clawback({
6505 .id = vaultKeylet.key,
6506 .holder = depositor,
6513 auto const sle = env.le(vaultKeylet);
6514 BEAST_EXPECT(sle !=
nullptr);
6515 BEAST_EXPECT(sle->at(sfAssetsAvailable) == iou(60).value());
6516 BEAST_EXPECT(sle->at(sfAssetsTotal) == iou(100).value());
6517 auto const sharesAfter = env.balance(depositor, shares);
6518 BEAST_EXPECT(sharesAfter == sharesBefore);
6521 env.enableFeature(fixCleanup3_1_3);
6532 Env env{*
this, testableAmendments()};
6534 Account const issuer{
"issuer"};
6536 Vault const vault{env};
6537 env.fund(XRP(1'000'000), issuer, owner);
6541 BEAST_EXPECT(maxInt64 ==
"9223372036854775807");
6545 BEAST_EXPECT(maxInt64Plus1 ==
"9223372036854775808");
6550 BEAST_EXPECT(maxInt64Plus2 ==
"9223372036854775809");
6553 BEAST_EXPECT(initialXRP ==
"100000000000000000");
6556 BEAST_EXPECT(initialXRPPlus1 ==
"100000000000000001");
6563 auto [tx,
keylet] = vault.create({.owner = owner, .asset = xrpAsset});
6564 tx[sfData] =
"4D65746144617461";
6566 tx[sfAssetsMaximum] = maxInt64;
6570 tx[sfAssetsMaximum] = initialXRPPlus1;
6574 tx[sfAssetsMaximum] = initialXRP;
6579 env.setParseFailureExpected(
true);
6582 tx[sfAssetsMaximum] = maxInt64Plus1;
6592 "invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
6597 tx[sfAssetsMaximum] = maxInt64Plus2;
6606 "invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
6612 auto const insertAt = maxInt64Plus2.size() - 3;
6613 auto const decimalTest = maxInt64Plus2.substr(0, insertAt) +
"." +
6614 maxInt64Plus2.substr(insertAt);
6615 BEAST_EXPECT(decimalTest ==
"9223372036854775.809");
6616 tx[sfAssetsMaximum] = decimalTest;
6625 "invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
6628 auto const vaultSle = env.le(newKeylet);
6629 BEAST_EXPECT(!vaultSle);
6636 MPTTester mptt{env, issuer, kMptInitNoFund};
6637 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
6640 mptt.authorize({.account = owner});
6645 env(pay(issuer, owner, mptAsset(100'000)));
6648 auto [tx,
keylet] = vault.create({.owner = owner, .asset = mptAsset});
6649 tx[sfData] =
"4D65746144617461";
6651 tx[sfAssetsMaximum] = maxInt64;
6655 tx[sfAssetsMaximum] = initialXRPPlus1;
6659 tx[sfAssetsMaximum] = initialXRP;
6665 tx[sfAssetsMaximum] = maxInt64Plus2;
6674 "invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
6680 auto const insertAt = maxInt64Plus2.size() - 1;
6681 auto const decimalTest = maxInt64Plus2.substr(0, insertAt) +
"." +
6682 maxInt64Plus2.substr(insertAt);
6683 BEAST_EXPECT(decimalTest ==
"922337203685477580.9");
6684 tx[sfAssetsMaximum] = decimalTest;
6693 "invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
6696 auto const vaultSle = env.le(newKeylet);
6697 BEAST_EXPECT(!vaultSle);
6705 env.trust(iouAsset(1000), owner);
6706 env(pay(issuer, owner, iouAsset(200)));
6709 auto [tx,
keylet] = vault.create({.owner = owner, .asset = iouAsset});
6710 tx[sfData] =
"4D65746144617461";
6712 tx[sfAssetsMaximum] = maxInt64;
6716 tx[sfAssetsMaximum] = initialXRPPlus1;
6720 tx[sfAssetsMaximum] = initialXRP;
6726 env.setParseFailureExpected(
true);
6729 tx[sfAssetsMaximum] = maxInt64Plus2;
6738 "invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
6741 tx[sfAssetsMaximum] =
"1000000000000000e80";
6744 tx[sfAssetsMaximum] =
"1000000000000000e-96";
6749 auto const newKeylet =
6753 auto const insertAt = maxInt64Plus2.size() - 1;
6754 auto const decimalTest = maxInt64Plus2.substr(0, insertAt) +
"." +
6755 maxInt64Plus2.substr(insertAt);
6756 BEAST_EXPECT(decimalTest ==
"922337203685477580.9");
6757 tx[sfAssetsMaximum] = decimalTest;
6766 "invalidParamsField 'tx_json.AssetsMaximum' has invalid data.");
6769 auto const vaultSle = env.le(newKeylet);
6770 BEAST_EXPECT(!vaultSle);
6773 tx[sfAssetsMaximum] =
"9223372036854775807e40";
6774 auto const newKeylet =
6779 auto const vaultSle = env.le(newKeylet);
6780 if (!BEAST_EXPECT(vaultSle))
6784 (vaultSle->at(sfAssetsMaximum) ==
6785 Number{9223372036854776, 43, Number::Normalized{}}));
6788 tx[sfAssetsMaximum] =
"9223372036854775807e-40";
6789 auto const newKeylet =
6794 auto const vaultSle = env.le(newKeylet);
6795 if (!BEAST_EXPECT(vaultSle))
6799 (vaultSle->at(sfAssetsMaximum) ==
6800 Number{9223372036854776, -37, Number::Normalized{}}));
6803 tx[sfAssetsMaximum] =
"9223372036854775807e-100";
6804 auto const newKeylet =
6810 auto const vaultSle = env.
le(newKeylet);
6811 if (!BEAST_EXPECT(vaultSle))
6814 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) ==
kNumZero);
6819 tx[sfAssetsMaximum] =
"1000000000000000e81";
6827 tx[sfAssetsMaximum] =
"18446744073709551617e5";
6829 BEAST_EXPECTS(
false,
"Expected parse_error for mantissa larger than uint64 max");
6831 catch (ParseError
const& e)
6833 using namespace std::string_literals;
6835 e.what() ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data."s);
6853 testcase(
"Vault deposit fails when MPT asset is escrowed");
6855 Env env{*
this, testableAmendments()};
6856 auto const baseFee = env.
current()->fees().base;
6858 Account const depositor{
"depositor"};
6859 Account const issuer{
"issuer"};
6862 env.fund(XRP(10000), issuer, owner, depositor, bob);
6865 MPTTester mptt{env, issuer, kMptInitNoFund};
6867 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow});
6868 mptt.authorize({.account = owner});
6869 mptt.authorize({.account = depositor});
6870 mptt.authorize({.account = bob});
6872 env(pay(issuer, depositor, asset(100)));
6876 auto const escrowSeq = env.seq(depositor);
6877 env(escrow::create(depositor, bob, asset(60)),
6878 escrow::kCondition(escrow::kCb1),
6879 escrow::kFinishTime(env.now() + 1s),
6884 Vault const vault{env};
6885 auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
6891 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
6896 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(40)}),
6901 auto const sle = env.le(vaultKeylet);
6902 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
6906 env(escrow::finish(bob, depositor, escrowSeq),
6907 escrow::kCondition(escrow::kCb1),
6908 escrow::kFulfillment(escrow::kFb1),
6915 testcase(
"Vault withdraw respects escrowed shares");
6917 Env env{*
this, testableAmendments()};
6918 auto const baseFee = env.
current()->fees().base;
6920 Account const depositor{
"depositor"};
6921 Account const issuer{
"issuer"};
6924 env.fund(XRP(10000), issuer, owner, depositor, bob);
6927 MPTTester mptt{env, issuer, kMptInitNoFund};
6929 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow});
6930 mptt.authorize({.account = owner});
6931 mptt.authorize({.account = depositor});
6933 env(pay(issuer, depositor, asset(100)));
6936 Vault const vault{env};
6937 auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
6943 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
6947 auto const vaultSle = env.le(vaultKeylet);
6948 if (!BEAST_EXPECT(vaultSle))
6950 env.memoize(
Account(
"vault", vaultSle->at(sfAccount)));
6954 auto const shareMPTID = vaultSle->at(sfShareMPTID);
6957 jv[jss::Account] = bob.human();
6958 jv[sfMPTokenIssuanceID] =
to_string(shareMPTID);
6959 jv[jss::TransactionType] = jss::MPTokenAuthorize;
6965 auto const escrowAmount = shares(
Number{6, vaultSle->at(sfScale) + 1});
6966 env(escrow::create(depositor, bob, escrowAmount),
6967 escrow::kCondition(escrow::kCb1),
6968 escrow::kFinishTime(env.now() + 1s),
6975 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
6981 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(40)}),
6986 auto const sle = env.le(vaultKeylet);
6987 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(60).value());
6992 testcase(
"Vault clawback only recovers unlocked shares");
6994 Env env{*
this, testableAmendments() | fixCleanup3_1_3};
6995 auto const baseFee = env.
current()->fees().base;
6997 Account const depositor{
"depositor"};
6998 Account const issuer{
"issuer"};
7001 env.fund(XRP(10000), issuer, owner, depositor, bob);
7004 MPTTester mptt{env, issuer, kMptInitNoFund};
7006 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow});
7007 mptt.authorize({.account = owner});
7008 mptt.authorize({.account = depositor});
7010 env(pay(issuer, depositor, asset(100)));
7013 Vault const vault{env};
7014 auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
7020 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
7024 auto const vaultSle = env.le(vaultKeylet);
7025 if (!BEAST_EXPECT(vaultSle))
7027 env.memoize(
Account(
"vault", vaultSle->at(sfAccount)));
7031 auto const shareMPTID = vaultSle->at(sfShareMPTID);
7034 jv[jss::Account] = bob.human();
7035 jv[sfMPTokenIssuanceID] =
to_string(shareMPTID);
7036 jv[jss::TransactionType] = jss::MPTokenAuthorize;
7042 auto const escrowAmount = shares(
Number{6, vaultSle->at(sfScale) + 1});
7043 env(escrow::create(depositor, bob, escrowAmount),
7044 escrow::kCondition(escrow::kCb1),
7045 escrow::kFinishTime(env.now() + 1s),
7052 env(vault.clawback({
7054 .id = vaultKeylet.key,
7055 .holder = depositor,
7061 auto const sle = env.le(vaultKeylet);
7062 BEAST_EXPECT(sle !=
nullptr);
7064 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(60).value());
7065 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
7068 auto const sharesAfter = env.balance(depositor, shares);
7069 BEAST_EXPECT(sharesAfter == shares(0));
8405 auto readReferenceHolding = [&](Env
const& env,
8407 auto const sleVault = env.le(vaultKeylet);
8409 return std::nullopt;
8411 if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding))
8412 return std::nullopt;
8413 return sleIssuance->getFieldH256(sfReferenceHolding);
8420 testcase(
"sfReferenceHolding: MPT-backed vault, post-amendment");
8421 Env env{*
this, testableAmendments()};
8422 Account const issuer{
"issuer"};
8424 env.fund(XRP(10'000), issuer, owner);
8427 MPTTester mptt{env, issuer, kMptInitNoFund};
8428 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
8430 mptt.authorize({.account = owner});
8432 Vault const vault{env};
8433 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8437 auto const sleVault = env.le(
keylet);
8438 BEAST_EXPECT(sleVault !=
nullptr);
8439 auto const pseudoId = sleVault->at(sfAccount);
8440 auto const expected =
keylet::mptoken(mptt.issuanceID(), pseudoId).key;
8442 auto const stored = readReferenceHolding(env,
keylet);
8443 BEAST_EXPECT(stored.has_value());
8444 BEAST_EXPECT(stored && *stored == expected);
8446 BEAST_EXPECT(env.le(
keylet::mptoken(mptt.issuanceID(), pseudoId)) !=
nullptr);
8450 testcase(
"sfReferenceHolding: IOU-backed vault, post-amendment");
8451 Env env{*
this, testableAmendments()};
8452 Account const issuer{
"issuer"};
8454 env.fund(XRP(10'000), issuer, owner);
8455 env(fset(issuer, asfDefaultRipple));
8459 env.trust(asset(1'000'000), owner);
8462 Vault const vault{env};
8463 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8467 auto const sleVault = env.le(
keylet);
8468 BEAST_EXPECT(sleVault !=
nullptr);
8469 auto const pseudoId = sleVault->at(sfAccount);
8472 auto const stored = readReferenceHolding(env,
keylet);
8473 BEAST_EXPECT(stored.has_value());
8474 BEAST_EXPECT(stored && *stored == expected);
8482 testcase(
"sfReferenceHolding: XRP-backed vault, field absent");
8483 Env env{*
this, testableAmendments()};
8485 env.fund(XRP(10'000), owner);
8489 Vault const vault{env};
8490 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8494 BEAST_EXPECT(!readReferenceHolding(env,
keylet).has_value());
8500 testcase(
"sfReferenceHolding: vault share, pre-amendment");
8501 Env env{*
this, testableAmendments() - fixCleanup3_2_0};
8502 Account const issuer{
"issuer"};
8504 env.fund(XRP(10'000), issuer, owner);
8507 MPTTester mptt{env, issuer, kMptInitNoFund};
8508 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
8510 mptt.authorize({.account = owner});
8512 Vault const vault{env};
8513 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8517 BEAST_EXPECT(!readReferenceHolding(env,
keylet).has_value());
8524 testcase(
"sfReferenceHolding: plain MPT issuance never set");
8525 Env env{*
this, testableAmendments()};
8526 Account const issuer{
"issuer"};
8527 env.fund(XRP(10'000), issuer);
8530 MPTTester mptt{env, issuer, kMptInitNoFund};
8531 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
8535 if (BEAST_EXPECT(sleIssuance))
8536 BEAST_EXPECT(!sleIssuance->isFieldPresent(sfReferenceHolding));
8556 auto referencedHoldingExists = [&](Env
const& env,
Keylet const& vaultKeylet) ->
bool {
8557 auto const sleVault = env.le(vaultKeylet);
8561 if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding))
8563 auto const holdingKey = sleIssuance->getFieldH256(sfReferenceHolding);
8569 testcase(
"vault pseudo MPToken: Clawback blocked by tecPSEUDO_ACCOUNT");
8570 Env env{*
this, testableAmendments()};
8571 Account const issuer{
"issuer"};
8573 Account const depositor{
"depositor"};
8574 env.fund(XRP(10'000), issuer, owner, depositor);
8577 MPTTester mptt{env, issuer, kMptInitNoFund};
8578 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback});
8580 mptt.authorize({.account = owner});
8581 mptt.authorize({.account = depositor});
8582 env(pay(issuer, depositor, asset(1'000)));
8585 Vault const vault{env};
8586 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8590 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)}));
8593 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8595 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
8605 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8607 BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500);
8611 testcase(
"vault pseudo MPToken: Issuer cannot Unauthorize pseudo");
8612 Env env{*
this, testableAmendments()};
8613 Account const issuer{
"issuer"};
8615 env.fund(XRP(10'000), issuer, owner);
8618 MPTTester mptt{env, issuer, kMptInitNoFund};
8619 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
8621 mptt.authorize({.account = owner});
8622 mptt.authorize({.account = issuer, .holder = owner});
8625 Vault const vault{env};
8626 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8630 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8632 auto const pseudoId = env.le(
keylet)->at(sfAccount);
8640 jv[sfAccount] = issuer.human();
8642 jv[sfMPTokenIssuanceID] =
to_string(mptt.issuanceID());
8643 jv[sfFlags] = tfMPTUnauthorize;
8644 jv[sfTransactionType] = jss::MPTokenAuthorize;
8647 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8651 testcase(
"vault pseudo MPToken: MPTokenIssuanceDestroy blocked while vault holds");
8652 Env env{*
this, testableAmendments()};
8653 Account const issuer{
"issuer"};
8655 Account const depositor{
"depositor"};
8656 env.fund(XRP(10'000), issuer, owner, depositor);
8659 MPTTester mptt{env, issuer, kMptInitNoFund};
8660 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
8662 mptt.authorize({.account = owner});
8663 mptt.authorize({.account = depositor});
8664 env(pay(issuer, depositor, asset(1'000)));
8667 Vault const vault{env};
8668 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8672 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)}));
8675 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8684 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8689 testcase(
"vault pseudo trust line: Clawback blocked by tecPSEUDO_ACCOUNT");
8690 Env env{*
this, testableAmendments()};
8691 Account const issuer{
"issuer"};
8693 env.fund(XRP(10'000), issuer, owner);
8694 env(fset(issuer, asfAllowTrustLineClawback));
8698 env.trust(asset(1'000'000), owner);
8699 env(pay(issuer, owner, asset(1'000)));
8702 Vault const vault{env};
8703 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8707 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)}));
8710 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8712 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
8723 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8725 BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500);
8729 testcase(
"vault pseudo trust line: TrustSet limit=0 from issuer preserves line");
8730 Env env{*
this, testableAmendments()};
8731 Account const issuer{
"issuer"};
8733 env.fund(XRP(10'000), issuer, owner);
8734 env(fset(issuer, asfDefaultRipple));
8738 env.trust(asset(1'000'000), owner);
8739 env(pay(issuer, owner, asset(1'000)));
8742 Vault const vault{env};
8743 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8747 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)}));
8750 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8758 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
8759 env(trust(issuer, pseudoAccount[
"IOU"](0)));
8761 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8766 testcase(
"vault pseudo holding: VaultDelete is the legitimate cleanup path");
8767 Env env{*
this, testableAmendments()};
8768 Account const issuer{
"issuer"};
8770 env.fund(XRP(10'000), issuer, owner);
8773 MPTTester mptt{env, issuer, kMptInitNoFund};
8774 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
8776 mptt.authorize({.account = owner});
8778 Vault const vault{env};
8779 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
8783 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
8784 auto const pseudoId = env.le(
keylet)->at(sfAccount);
8785 auto const sharedMptId = env.le(
keylet)->at(sfShareMPTID);
8786 auto const holdingKeylet =
keylet::mptoken(mptt.issuanceID(), pseudoId);
8791 env(vault.del({.owner = owner, .id = keylet.key}));
8794 BEAST_EXPECT(env.le(
keylet) ==
nullptr);
8795 BEAST_EXPECT(env.le(holdingKeylet) ==
nullptr);
9028 testcase(
"VaultDeposit IOU freeze checks");
9030 Account const issuer{
"issuer"};
9035 env.fund(XRP(100'000), issuer, owner);
9036 env(fset(issuer, asfAllowTrustLineClawback));
9039 env.trust(asset(1'000'000), owner);
9040 env(pay(issuer, owner, asset(100'000)));
9043 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
9046 auto const vaultAcct =
Account(
"vault", env.le(
keylet)->at(sfAccount));
9049 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
9052 auto runTests = [&]() {
9053 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
9057 testcase(
"VaultDeposit IOU global freeze");
9058 env(fset(issuer, asfGlobalFreeze));
9059 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9061 env(fclear(issuer, asfGlobalFreeze));
9066 testcase(
"VaultDeposit IOU depositor freeze");
9067 env(trust(issuer, asset(0), owner, tfSetFreeze));
9068 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9070 env(trust(issuer, asset(0), owner, tfClearFreeze));
9075 testcase(
"VaultDeposit IOU depositor deep freeze");
9076 env(trust(issuer, asset(0), owner, tfSetFreeze | tfSetDeepFreeze));
9077 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9079 env(trust(issuer, asset(0), owner, tfClearFreeze | tfClearDeepFreeze));
9087 testcase(
"VaultDeposit IOU pseudo-account freeze");
9088 auto trustSet = [&]() {
9090 jv[jss::Account] = issuer.human();
9092 auto& ja = jv[jss::LimitAmount] =
9094 ja[jss::issuer] =
toBase58(vaultAcct.id());
9096 jv[jss::TransactionType] = jss::TrustSet;
9100 trustSet[jss::Flags] = tfSetFreeze;
9105 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9108 trustSet[jss::Flags] = tfClearFreeze;
9115 testcase(
"VaultDeposit IOU pseudo-account deep freeze");
9116 auto trustSet = [&]() {
9118 jv[jss::Account] = issuer.human();
9120 auto& ja = jv[jss::LimitAmount] =
9122 ja[jss::issuer] =
toBase58(vaultAcct.id());
9124 jv[jss::TransactionType] = jss::TrustSet;
9128 trustSet[jss::Flags] = tfSetFreeze | tfSetDeepFreeze;
9132 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9135 trustSet[jss::Flags] = tfClearFreeze | tfClearDeepFreeze;
9142 testcase(
"VaultDeposit IOU freeze clawback unaffected");
9143 env(fset(issuer, asfGlobalFreeze));
9145 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(1)}));
9146 env(fclear(issuer, asfGlobalFreeze));
9147 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
9153 env.disableFeature(fixCleanup3_3_0);
9155 env.enableFeature(fixCleanup3_3_0);
9325 testcase(
"VaultWithdraw IOU freeze checks");
9327 Account const issuer{
"issuer"};
9330 Vault const vault{env};
9332 env.fund(XRP(100'000), issuer, owner);
9333 env(fset(issuer, asfAllowTrustLineClawback));
9336 env.trust(asset(1'000'000), owner);
9337 env(pay(issuer, owner, asset(100'000)));
9340 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
9343 auto const vaultAcct =
Account(
"vault", env.le(
keylet)->at(sfAccount));
9345 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
9348 Account const charlie{
"charlie"};
9349 env.fund(XRP(10'000), charlie);
9350 env.trust(asset(1'000'000), charlie);
9353 auto runTests = [&]() {
9354 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
9357 testcase(
"VaultWithdraw IOU global freeze");
9358 env(fset(issuer, asfGlobalFreeze));
9359 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9363 auto withdrawToCharlie =
9364 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
9365 withdrawToCharlie[sfDestination] = charlie.human();
9368 env(fclear(issuer, asfGlobalFreeze));
9373 testcase(
"VaultWithdraw IOU pseudo-account freeze");
9374 auto trustSet = [&]() {
9376 jv[jss::Account] = issuer.human();
9378 auto& ja = jv[jss::LimitAmount] =
9380 ja[jss::issuer] =
toBase58(vaultAcct.id());
9382 jv[jss::TransactionType] = jss::TrustSet;
9386 trustSet[jss::Flags] = tfSetFreeze;
9393 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9397 auto withdrawToCharlie =
9398 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
9399 withdrawToCharlie[sfDestination] = charlie.human();
9400 env(withdrawToCharlie, Ter(terExpected));
9402 trustSet[jss::Flags] = tfClearFreeze;
9409 testcase(
"VaultWithdraw IOU self-withdraw freeze check");
9410 env(trust(issuer, asset(0), owner, tfSetFreeze));
9414 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9418 auto withdrawTo3rd =
9419 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
9420 withdrawTo3rd[sfDestination] = charlie.human();
9427 env(trust(issuer, asset(0), owner, tfClearFreeze));
9431 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
9438 testcase(
"VaultWithdraw IOU depositor deep freeze");
9439 env(trust(issuer, asset(0), owner, tfSetFreeze | tfSetDeepFreeze));
9441 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
9444 env(trust(issuer, asset(0), owner, tfClearFreeze | tfClearDeepFreeze));
9449 testcase(
"VaultWithdraw IOU freeze withdraw to 3rd party");
9451 env(trust(issuer, asset(0), charlie, tfSetFreeze));
9454 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
9456 auto withdrawToCharlie =
9457 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
9458 withdrawToCharlie[sfDestination] = charlie.human();
9464 env(trust(issuer, asset(0), charlie, tfClearFreeze));
9468 {.depositor = owner,
9470 .amount = asset(fix330Enabled ? 2 : 1)}));
9476 testcase(
"VaultWithdraw IOU deep freeze withdraw to 3rd party");
9478 env(trust(issuer, asset(0), charlie, tfSetFreeze | tfSetDeepFreeze));
9480 auto withdrawToCharlie =
9481 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
9482 withdrawToCharlie[sfDestination] = charlie.human();
9486 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
9488 env(trust(issuer, asset(0), charlie, tfClearFreeze | tfClearDeepFreeze));
9489 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
9495 testcase(
"VaultWithdraw IOU freeze clawback unaffected");
9496 env(fset(issuer, asfGlobalFreeze));
9499 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(1)}));
9501 env(fclear(issuer, asfGlobalFreeze));
9502 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
9508 env.disableFeature(fixCleanup3_3_0);
9510 env.enableFeature(fixCleanup3_3_0);
9517 testcase(
"VaultWithdraw MPT lock checks");
9519 Account const issuer{
"issuer"};
9524 env.fund(XRP(100'000), issuer, owner);
9527 MPTTester mptt{env, issuer, kMptInitNoFund};
9529 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
9532 mptt.authorize({.account = owner});
9533 mptt.authorize({.account = issuer, .holder = owner});
9535 env(pay(issuer, owner, mpt(100'000)));
9538 auto [tx,
keylet] = vault.create({.owner = owner, .asset = mpt});
9541 Account const vaultAcct(
"vault", env.le(
keylet)->at(sfAccount));
9543 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(100)}));
9546 Account const charlie{
"charlie"};
9547 env.fund(XRP(10'000), charlie);
9549 mptt.authorize({.account = charlie});
9550 mptt.authorize({.account = issuer, .holder = charlie});
9553 auto runTests = [&]() {
9554 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
9558 testcase(
"VaultWithdraw MPT global lock");
9559 mptt.set({.flags = tfMPTLock});
9561 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
9569 auto withdrawToIssuer =
9570 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
9571 withdrawToIssuer[sfDestination] = issuer.human();
9574 mptt.set({.flags = tfMPTUnlock});
9578 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
9585 testcase(
"VaultWithdraw MPT pseudo-account lock");
9586 mptt.set({.holder = vaultAcct, .flags = tfMPTLock});
9588 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
9590 mptt.set({.holder = vaultAcct, .flags = tfMPTUnlock});
9597 testcase(
"VaultWithdraw MPT depositor lock");
9598 mptt.set({.holder = owner, .flags = tfMPTLock});
9600 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
9604 auto withdrawToCharlie =
9605 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
9606 withdrawToCharlie[sfDestination] = charlie.human();
9614 auto withdrawToIssuer =
9615 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
9616 withdrawToIssuer[sfDestination] = issuer.human();
9619 mptt.set({.holder = owner, .flags = tfMPTUnlock});
9623 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
9630 testcase(
"VaultWithdraw MPT 3rd party destination lock");
9631 mptt.set({.holder = charlie, .flags = tfMPTLock});
9634 auto withdrawToCharlie =
9635 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
9636 withdrawToCharlie[sfDestination] = charlie.human();
9640 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
9641 mptt.set({.holder = charlie, .flags = tfMPTUnlock});
9643 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
9649 testcase(
"VaultWithdraw MPT lock clawback unaffected");
9650 mptt.set({.flags = tfMPTLock});
9653 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = mpt(1)}));
9654 mptt.set({.flags = tfMPTUnlock});
9656 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
9662 env.disableFeature(fixCleanup3_3_0);
9664 env.enableFeature(fixCleanup3_3_0);