88 Account const depositor{
"depositor"};
89 Account const charlie{
"charlie"};
92 auto const testSequence = [&,
this](
97 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
98 tx[sfData] =
"AFEED00E";
99 tx[sfAssetsMaximum] = asset(100).number();
102 BEAST_EXPECT(env.le(
keylet));
105 auto const [share, vaultAccount] =
107 auto const vault = env.le(
keylet);
108 BEAST_EXPECT(vault !=
nullptr);
109 if (!asset.integral())
111 BEAST_EXPECT(vault->at(sfScale) == 6);
115 BEAST_EXPECT(vault->at(sfScale) == 0);
118 BEAST_EXPECT(shares !=
nullptr);
119 if (!asset.integral())
121 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
125 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
127 return {
MPTIssue(vault->at(sfShareMPTID)),
Account(
"vault", vault->at(sfAccount))};
129 auto const shares = share.raw().get<
MPTIssue>();
130 env.memoize(vaultAccount);
135 env.fund(XRP(1000), alice, erin);
136 env(fset(alice, asfDepositAuth));
140 testcase(prefix +
" fail to deposit more than assets held");
141 auto tx = vault.deposit(
142 {.depositor = depositor, .id =
keylet.key, .amount = asset(10000)});
148 testcase(prefix +
" deposit non-zero amount");
150 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
153 BEAST_EXPECT(env.balance(depositor, shares) == share(50 *
scale));
157 testcase(prefix +
" deposit non-zero amount again");
159 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
162 BEAST_EXPECT(env.balance(depositor, shares) == share(100 *
scale));
166 testcase(prefix +
" fail to delete non-empty vault");
167 auto tx = vault.del({.owner = owner, .id =
keylet.key});
173 testcase(prefix +
" fail to update because wrong owner");
174 auto tx = vault.set({.owner = issuer, .id =
keylet.key});
175 tx[sfAssetsMaximum] = asset(50).number();
181 testcase(prefix +
" fail to set maximum lower than current amount");
182 auto tx = vault.set({.owner = owner, .id =
keylet.key});
183 tx[sfAssetsMaximum] = asset(50).number();
189 testcase(prefix +
" set maximum higher than current amount");
190 auto tx = vault.set({.owner = owner, .id =
keylet.key});
191 tx[sfAssetsMaximum] = asset(150).number();
197 testcase(prefix +
" set maximum is idempotent, set it again");
198 auto tx = vault.set({.owner = owner, .id =
keylet.key});
199 tx[sfAssetsMaximum] = asset(150).number();
206 auto tx = vault.set({.owner = owner, .id =
keylet.key});
213 testcase(prefix +
" fail to set domain on public vault");
214 auto tx = vault.set({.owner = owner, .id =
keylet.key});
221 testcase(prefix +
" fail to deposit more than maximum");
223 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
229 testcase(prefix +
" reset maximum to zero i.e. not enforced");
230 auto tx = vault.set({.owner = owner, .id =
keylet.key});
231 tx[sfAssetsMaximum] = asset(0).number();
237 testcase(prefix +
" fail to withdraw more than assets held");
238 auto tx = vault.withdraw(
239 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
245 testcase(prefix +
" deposit some more");
247 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
250 BEAST_EXPECT(env.balance(depositor, shares) == share(200 *
scale));
254 testcase(prefix +
" clawback some");
256 auto tx = vault.clawback(
257 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(10)});
260 if (!asset.raw().native())
262 BEAST_EXPECT(env.balance(depositor, shares) == share(190 *
scale));
269 auto tx = vault.clawback({.issuer = issuer, .id =
keylet.key, .holder = depositor});
272 if (!asset.raw().native())
274 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
277 auto tx = vault.clawback(
281 .amount = asset(10)});
287 auto tx = vault.withdraw(
288 {.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
295 if (!asset.raw().native())
297 testcase(prefix +
" deposit again");
299 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(200)});
302 BEAST_EXPECT(env.balance(depositor, shares) == share(200 *
scale));
306 testcase(prefix +
" deposit/withdrawal same or less than fee");
307 auto const amount = env.current()->fees().base;
310 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount});
314 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = amount});
318 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount});
323 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = amount});
324 tx[sfDestination] = charlie.human();
329 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = amount - 1});
334 {.depositor = depositor, .id =
keylet.key, .amount = amount - 1});
340 testcase(prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
341 auto tx = vault.withdraw(
342 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
343 tx[sfDestination] = alice.human();
349 testcase(prefix +
" fail to withdraw to zero destination");
350 auto tx = vault.withdraw(
351 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
352 tx[sfDestination] =
"0";
357 if (!asset.raw().native())
359 testcase(prefix +
" fail to withdraw to 3rd party no authorization");
360 auto tx = vault.withdraw(
361 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
362 tx[sfDestination] = erin.human();
368 testcase(prefix +
" fail to withdraw to 3rd party lsfRequireDestTag");
369 auto tx = vault.withdraw(
370 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
371 tx[sfDestination] = dave.human();
377 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
379 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
380 tx[sfDestination] = dave.human();
381 tx[sfDestinationTag] =
"0";
387 testcase(prefix +
" deposit again");
388 auto tx = vault.deposit({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
394 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
396 vault.withdraw({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
402 testcase(prefix +
" withdraw with tag");
404 vault.withdraw({.depositor = dave, .id =
keylet.key, .amount = asset(50)});
405 tx[sfDestinationTag] =
"0";
411 testcase(prefix +
" withdraw to authorized 3rd party");
413 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
414 tx[sfDestination] = charlie.human();
417 BEAST_EXPECT(env.balance(depositor, shares) == share(100 *
scale));
421 testcase(prefix +
" withdraw to issuer");
423 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
424 tx[sfDestination] = issuer.human();
427 BEAST_EXPECT(env.balance(depositor, shares) == share(50 *
scale));
430 if (!asset.raw().native())
432 testcase(prefix +
" issuer deposits");
434 vault.deposit({.depositor = issuer, .id =
keylet.key, .amount = asset(10)});
437 BEAST_EXPECT(env.balance(issuer, shares) == share(10 *
scale));
439 testcase(prefix +
" issuer withdraws");
441 {.depositor = issuer, .id =
keylet.key, .amount = share(10 *
scale)});
444 BEAST_EXPECT(env.balance(issuer, shares) == share(0 *
scale));
448 testcase(prefix +
" withdraw remaining assets");
450 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
453 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
455 if (!asset.raw().native())
457 auto tx = vault.clawback(
461 .amount = asset(0)});
467 auto tx = vault.withdraw(
468 {.depositor = depositor, .id =
keylet.key, .amount = share(10)});
474 if (!asset.integral())
476 testcase(prefix +
" temporary authorization for 3rd party");
477 env(trust(erin, asset(1000)));
478 env(trust(issuer, asset(0), erin, tfSetfAuth));
479 env(pay(issuer, erin, asset(10)));
482 auto tx = vault.deposit({.depositor = erin, .id =
keylet.key, .amount = asset(10)});
486 auto tx = pay(erin, depositor, share(10 *
scale));
494 {.depositor = depositor, .id = keylet.key, .amount = asset(1)}));
501 testcase(prefix +
" withdraw to authorized 3rd party");
504 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
505 tx[sfDestination] = erin.human();
510 env(pay(erin, issuer, asset(10)));
513 testcase(prefix +
" fail to pay to unauthorized 3rd party");
514 env(trust(erin, asset(0)));
518 env(pay(depositor, erin, share(1)), Ter{
tecNO_LINE});
522 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(1)});
528 testcase(prefix +
" fail to delete because wrong owner");
529 auto tx = vault.del({.owner = issuer, .id =
keylet.key});
535 testcase(prefix +
" delete empty vault");
536 auto tx = vault.del({.owner = owner, .id =
keylet.key});
539 BEAST_EXPECT(!env.le(
keylet));
543 auto testCases = [&,
this](
545 Env env{*
this, testableAmendments()};
548 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
550 env(fset(issuer, asfAllowTrustLineClawback));
551 env(fset(issuer, asfRequireAuth));
552 env(fset(dave, asfRequireDest));
554 env.require(Flags(issuer, asfAllowTrustLineClawback));
555 env.require(Flags(issuer, asfRequireAuth));
558 testSequence(prefix, env, vault, asset);
563 testCases(
"IOU", [&](Env& env) ->
Asset {
565 env(trust(owner, asset(1000)));
566 env(trust(depositor, asset(1000)));
567 env(trust(charlie, asset(1000)));
568 env(trust(dave, asset(1000)));
569 env(trust(issuer, asset(0), owner, tfSetfAuth));
570 env(trust(issuer, asset(0), depositor, tfSetfAuth));
571 env(trust(issuer, asset(0), charlie, tfSetfAuth));
572 env(trust(issuer, asset(0), dave, tfSetfAuth));
573 env(pay(issuer, depositor, asset(1000)));
578 testCases(
"MPT", [&](Env& env) ->
Asset {
579 MPTTester mptt{env, issuer, kMptInitNoFund};
580 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
582 mptt.authorize({.account = depositor});
583 mptt.authorize({.account = charlie});
584 mptt.authorize({.account = dave});
585 env(pay(issuer, depositor, asset(1000)));
601 auto testCase = [&,
this](
608 CaseArgs args = {}) {
609 Env env{*
this, args.features};
610 Account const issuer{
"issuer"};
613 env.fund(XRP(1000), issuer, owner);
616 env(fset(issuer, asfAllowTrustLineClawback));
617 env(fset(issuer, asfRequireAuth));
621 env(trust(owner, asset(1000)));
622 env(trust(issuer, asset(0), owner, tfSetfAuth));
623 env(pay(issuer, owner, asset(1000)));
626 test(env, issuer, owner, asset, vault);
630 return [&, resultAfterCreate](
636 testcase(
"disabled single asset vault");
638 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
642 auto tx = vault.set({.owner = owner, .id =
keylet.key});
643 env(tx, kData(
"test"), Ter{resultAfterCreate});
648 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
649 env(tx, Ter{resultAfterCreate});
654 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
655 env(tx, Ter{resultAfterCreate});
659 auto tx = vault.clawback(
660 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(10)});
661 env(tx, Ter{resultAfterCreate});
665 auto tx = vault.del({.owner = owner, .id =
keylet.key});
666 env(tx, Ter{resultAfterCreate});
671 testCase(testDisabled(), {.features = testableAmendments() - featureSingleAssetVault});
673 testCase(testDisabled(
tecNO_ENTRY), {.features = testableAmendments() - featureMPTokensV1});
681 testcase(
"disabled permissioned domains");
683 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
686 tx[sfFlags] = tx[sfFlags].asUInt() | tfVaultPrivate;
691 auto tx = vault.set({.owner = owner, .id =
keylet.key});
692 env(tx, kData(
"Test"));
698 {.features = testableAmendments() - featurePermissionedDomains});
700 testCase([&](Env& env,
707 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
708 tx[sfFlags] = tfClearDeepFreeze;
712 auto tx = vault.set({.owner = owner, .id =
keylet.key});
713 tx[sfFlags] = tfClearDeepFreeze;
719 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
720 tx[sfFlags] = tfClearDeepFreeze;
726 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
727 tx[sfFlags] = tfClearDeepFreeze;
732 auto tx = vault.clawback(
733 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(10)});
734 tx[sfFlags] = tfClearDeepFreeze;
739 auto tx = vault.del({.owner = owner, .id =
keylet.key});
740 tx[sfFlags] = tfClearDeepFreeze;
745 testCase([&](Env& env,
752 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
757 auto tx = vault.set({.owner = owner, .id =
keylet.key});
764 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
771 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
777 auto tx = vault.clawback(
778 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(10)});
784 auto tx = vault.del({.owner = owner, .id =
keylet.key});
792 testcase(
"disabled permissioned domain");
794 auto [tx,
keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
799 auto tx = vault.set({.owner = owner, .id =
keylet.key});
805 auto tx = vault.set({.owner = owner, .id =
keylet.key});
806 tx[sfDomainID] =
"0";
810 {.features = (testableAmendments()) - featurePermissionedDomains});
812 testCase([&](Env& env,
819 auto [tx,
keylet] = vault.create({.owner = owner, .asset =
xrpIssue()});
822 auto tx = vault.set({
831 vault.deposit({.depositor = owner, .id = beast::kZero, .amount = asset(10)});
837 vault.withdraw({.depositor = owner, .id = beast::kZero, .amount = asset(10)});
842 auto tx = vault.clawback(
843 {.issuer = issuer, .id = beast::kZero, .holder = owner, .amount = asset(10)});
848 auto tx = vault.del({
858 testcase(
"withdraw to bad destination");
860 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
864 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
865 tx[jss::Destination] =
"0";
875 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
881 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
888 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
892 auto const sleVault = env.le(
keylet);
893 BEAST_EXPECT(sleVault);
894 BEAST_EXPECT((*sleVault)[sfScale] == 18);
898 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
902 auto const sleVault = env.le(
keylet);
903 BEAST_EXPECT(sleVault);
904 BEAST_EXPECT((*sleVault)[sfScale] == 0);
908 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
911 auto const sleVault = env.le(
keylet);
912 BEAST_EXPECT(sleVault);
913 BEAST_EXPECT((*sleVault)[sfScale] == 6);
919 testcase(
"create or set invalid data");
921 auto [tx1,
keylet] = vault.create({.owner = owner, .asset = asset});
937 auto tx = vault.set({.owner = owner, .id =
keylet.key});
943 auto tx = vault.set({.owner = owner, .id =
keylet.key});
954 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
957 auto tx = vault.set({.owner = owner, .id =
keylet.key});
964 testcase(
"create with invalid metadata");
966 auto [tx1,
keylet] = vault.create({.owner = owner, .asset = asset});
970 tx[sfMPTokenMetadata] =
"";
987 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
990 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1000 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1003 auto tx = vault.deposit(
1010 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(0)});
1017 testcase(
"invalid set immutable flag");
1019 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1022 auto tx = vault.set({.owner = owner, .id =
keylet.key});
1023 tx[sfFlags] = tfVaultPrivate;
1030 testcase(
"invalid withdraw amount");
1032 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1035 auto tx = vault.withdraw(
1042 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(0)});
1047 testCase([&](Env& env,
1054 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1059 auto tx = vault.clawback(
1060 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(50)});
1065 auto tx = vault.clawback(
1078 auto [tx1,
keylet] = vault.create({.owner = owner, .asset = asset});
1082 tx[sfWithdrawalPolicy] = 0;
1100 tx[sfFlags] = tfVaultPrivate;
1101 tx[sfDomainID] =
"0";
1574 bool enableClawback =
true;
1576 int initialXRP = 1000;
1580 auto testCase = [
this](
1588 MPTTester& mptt)>
test,
1589 CaseArgs args = {}) {
1590 Env env{*
this, args.features};
1591 Account const issuer{
"issuer"};
1593 Account const depositor{
"depositor"};
1594 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1598 MPTTester mptt{env, issuer, kMptInitNoFund};
1601 {.flags = tfMPTCanTransfer | tfMPTCanLock |
1602 (args.enableClawback ? tfMPTCanClawback : kNone) |
1603 (args.requireAuth ? tfMPTRequireAuth : kNone),
1606 mptt.authorize({.account = owner});
1607 mptt.authorize({.account = depositor});
1608 if (args.requireAuth)
1610 mptt.authorize({.account = issuer, .holder = owner});
1611 mptt.authorize({.account = issuer, .holder = depositor});
1614 env(pay(issuer, depositor, asset(1000)));
1617 test(env, issuer, owner, depositor, asset, vault, mptt);
1628 testcase(
"MPT nothing to clawback from");
1629 auto tx = vault.clawback(
1632 .holder = depositor,
1633 .amount = asset(10)});
1647 testcase(
"MPT global lock blocks create");
1648 mptt.set({.account = issuer, .flags = tfMPTLock});
1649 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1661 testcase(
"MPT only issuer can clawback");
1663 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1667 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
1672 auto tx = vault.clawback({
1673 .issuer = depositor,
1675 .holder = depositor,
1681 auto tx = vault.clawback({
1684 .holder = depositor,
1699 testcase(
"MPT depositor without MPToken, auth required");
1701 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1706 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
1712 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
1716 auto const sleMPT1 = env.le(mptoken);
1717 BEAST_EXPECT(sleMPT1 ==
nullptr);
1719 tx = vault.withdraw(
1720 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
1724 auto const sleMPT2 = env.le(mptoken);
1725 BEAST_EXPECT(sleMPT2 ==
nullptr);
1730 Account const charlie{
"charlie"};
1731 env.fund(XRP(1000), charlie);
1734 tx = vault.withdraw(
1735 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
1736 tx[sfDestination] = charlie.human();
1740 {.requireAuth =
true});
1751 testcase(
"MPT depositor without MPToken, no auth required");
1753 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1760 {.depositor = depositor,
1762 .amount = asset(1000)});
1768 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
1772 auto const sleMPT1 = env.le(mptoken);
1773 BEAST_EXPECT(sleMPT1 ==
nullptr);
1775 tx = vault.withdraw(
1776 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
1780 auto const sleMPT2 = env.le(mptoken);
1781 BEAST_EXPECT(sleMPT2 !=
nullptr);
1782 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
1787 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
1791 auto const sleMPT1 = env.le(mptoken);
1792 BEAST_EXPECT(sleMPT1 ==
nullptr);
1794 tx = vault.withdraw(
1795 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
1796 tx[sfDestination] = owner.human();
1800 auto const sleMPT2 = env.le(mptoken);
1801 BEAST_EXPECT(sleMPT2 ==
nullptr);
1804 {.requireAuth =
false});
1807 Env
const env{*
this, testableAmendments()};
1809 env.current()->fees().accountReserve(0).drops() /
kDropsPerXrp.drops(),
1810 env.current()->fees().increment.drops() /
kDropsPerXrp.drops()};
1822 testcase(
"MPT fail reserve to re-create MPToken");
1824 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1830 env(pay(depositor, owner, asset(1000)));
1834 {.depositor = owner,
1836 .amount = asset(1000)});
1842 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
1846 auto const sleMPT = env.le(mptoken);
1847 BEAST_EXPECT(sleMPT ==
nullptr);
1850 env(ticket::create(owner, 1));
1854 tx = vault.withdraw(
1855 {.depositor = owner, .id =
keylet.key, .amount = asset(100)});
1859 env(pay(depositor, owner, XRP(incReserve)));
1867 {.requireAuth =
false, .initialXRP = acctReserve + (incReserve * 4) + 1});
1879 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1883 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
1888 auto tx = vault.clawback(
1889 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
1893 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
1897 auto [tx,
keylet] = vault.create({.owner = depositor, .asset = asset});
1903 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
1909 vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(10)});
1914 auto tx = vault.clawback(
1915 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
1919 env(vault.del({.owner = owner, .id = keylet.key}));
1930 testcase(
"MPT vault owner can receive shares unless unauthorized");
1932 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
1936 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
1941 auto const vault = env.le(
keylet);
1942 return vault->at(sfShareMPTID);
1948 env(pay(depositor, owner, shares(1)));
1951 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = shares(1)});
1956 env(pay(depositor, owner, shares(1)));
1959 tx = vault.clawback(
1960 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(0)});
1965 env(pay(depositor, owner, shares(1)));
1969 env(pay(owner, depositor, shares(1)));
1975 jv[sfAccount] = owner.human();
1976 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
1977 jv[sfFlags] = tfMPTUnauthorize;
1978 jv[sfTransactionType] = jss::MPTokenAuthorize;
1984 tx = pay(depositor, owner, shares(1));
1989 tx = vault.clawback(
1990 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
1995 env(vault.del({.owner = owner, .id = keylet.key}));
2011 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2016 {.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
2021 auto tx = vault.clawback(
2024 .holder = depositor,
2025 .amount = asset(0)});
2029 {.enableClawback =
false});
2040 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2043 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1000)});
2047 mptt.authorize({.account = issuer, .holder = depositor, .flags = tfMPTUnauthorize});
2051 auto tx = vault.withdraw(
2052 {.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
2056 tx[sfDestination] = issuer.human();
2060 tx[sfDestination] = owner.human();
2068 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(100)});
2074 tx = vault.clawback(
2075 {.issuer = issuer, .id =
keylet.key, .holder = issuer, .amount = asset(800)});
2079 tx = vault.clawback(
2080 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(800)});
2084 env(vault.del({.owner = owner, .id = keylet.key}));
2090 Env env{*
this, testableAmendments()};
2092 Account const issuer{
"issuer"};
2093 env.fund(XRP(1000000), owner, issuer);
2095 Vault const vault{env};
2097 MPTTester mptt{env, issuer, kMptInitNoFund};
2099 {.flags = tfMPTCanTransfer | tfMPTCanLock | lsfMPTCanClawback | tfMPTRequireAuth});
2100 mptt.authorize({.account = owner});
2101 mptt.authorize({.account = issuer, .holder = owner});
2103 env(pay(issuer, owner, asset(100)));
2104 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2108 auto const shares = [&env,
keylet = k1,
this]() ->
Asset {
2109 auto const vault = env.le(
keylet);
2110 BEAST_EXPECT(vault !=
nullptr);
2111 return MPTIssue(vault->at(sfShareMPTID));
2114 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2120 testcase(
"MPT locked: vault shares inherit underlying lock");
2122 Env env{*
this, testableAmendments()};
2123 Account const issuer{
"issuer"};
2128 env.fund(XRP(10'000), issuer, owner, alice, bob, carol);
2130 Vault const vault{env};
2135 .holders = {owner, alice, bob, carol},
2136 .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanLock}};
2137 env(pay(issuer, alice, asset(1'000)));
2138 env(pay(issuer, bob, asset(1'000)));
2141 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2145 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(500)}));
2147 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(500)}));
2151 auto const sle = env.le(
keylet);
2152 BEAST_EXPECT(sle !=
nullptr);
2153 return MPTIssue(sle->at(sfShareMPTID));
2155 auto const shareMptID = shares.raw().get<
MPTIssue>().getMptID();
2156 auto const shareBalance = [&](
Account const& account) {
2158 return sle ? sle->at(sfMPTAmount) : 0;
2163 env(pay(alice, bob, shares(1)));
2168 env(offer(alice, XRP(1), shares(1)));
2172 asset.set({.account = issuer, .flags = tfMPTLock});
2177 BEAST_EXPECT(shareBalance(alice) == 499);
2178 BEAST_EXPECT(shareBalance(bob) == 501);
2179 env(pay(alice, bob, shares(1)), Ter{
tecLOCKED});
2181 BEAST_EXPECT(shareBalance(alice) == 499);
2182 BEAST_EXPECT(shareBalance(bob) == 501);
2186 env(pay(carol, bob, shares(1)),
2188 Path(BookSpec{shares.raw()}),
2191 BEAST_EXPECT(shareBalance(alice) == 499);
2192 BEAST_EXPECT(shareBalance(bob) == 501);
2193 BEAST_EXPECT(expectOffers(env, alice, 1));
2197 testcase(
"MPT CanTrade governance: share inherits underlying on DEX and AMM");
2199 Env env{*
this, testableAmendments()};
2200 Account const issuer{
"issuer"};
2204 env.fund(XRP(100'000), issuer, owner, alice, bob);
2206 Vault const vault{env};
2208 MPTTester mptt{env, issuer, kMptInitNoFund};
2210 {.flags = tfMPTCanTransfer | tfMPTCanLock,
2213 mptt.authorize({.account = owner});
2214 mptt.authorize({.account = alice});
2215 mptt.authorize({.account = bob});
2216 env(pay(issuer, alice, asset(10'000)));
2217 env(pay(issuer, bob, asset(10'000)));
2220 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2225 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(5'000)}));
2226 env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(5'000)}));
2230 auto const sle = env.le(
keylet);
2231 BEAST_EXPECT(sle !=
nullptr);
2232 return MPTIssue(sle->at(sfShareMPTID));
2242 AMM
const ammUnderlyingFail(
2244 AMM
const ammShares(env, alice, XRP(1'000), shares(100), Ter{
tecNO_PERMISSION});
2247 env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(100)}));
2252 env(pay(alice, bob, shares(1)));
2256 env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = asset(100)}));
2263 env(offer(alice, XRP(1), asset(10)));
2264 env(offer(alice, XRP(1), shares(1)));
2267 AMM
const ammUnderlying(env, alice, XRP(1'000), asset(1'000));
2271 testcase(
"MPT OutstandingAmount > MaximumAmount");
2273 Env env{*
this, testableAmendments() | featureSingleAssetVault};
2275 Account const issuer{
"issuer"};
2276 env.fund(XRP(1'000), alice, issuer);
2278 Vault const vault{env};
2280 MPTTester
const btc({.env = env, .issuer = issuer, .holders = {alice}, .maxAmt = 100});
2282 auto [tx, k] = vault.create({.owner = issuer, .asset = btc});
2286 tx = vault.deposit({.depositor = issuer, .id = k.key, .amount = btc(110)});
2293 env(pay(issuer, alice, btc(100)));
2296 tx = vault.deposit({.depositor = issuer, .id = k.key, .amount = btc(100)});
2301 tx = vault.deposit({.depositor = alice, .id = k.key, .amount = btc(100)});
2315 int initialXRP = 1000;
2318 bool charlieRipple =
true;
2322 auto testCase = [&,
this](
2332 CaseArgs args = {}) {
2333 Env env{*
this, args.features};
2335 Account const issuer{
"issuer"};
2336 Account const charlie{
"charlie"};
2338 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2339 env(fset(issuer, asfAllowTrustLineClawback));
2343 env.trust(asset(1000), owner);
2344 env(pay(issuer, owner, asset(args.initialIOU)));
2346 if (!args.charlieRipple)
2348 env(fset(issuer, 0, asfDefaultRipple));
2350 env.trust(asset(1000), charlie);
2352 env(pay(issuer, charlie, asset(args.initialIOU)));
2354 env(fset(issuer, asfDefaultRipple));
2358 env.trust(asset(1000), charlie);
2361 env(rate(issuer, args.transferRate));
2368 return env.le(
keylet)->at(sfShareMPTID);
2371 test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId);
2383 testcase(
"IOU cannot use different asset");
2386 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2392 auto tx = [&, account = vaultAccount(
keylet)]() {
2394 jv[jss::Account] = issuer.human();
2396 auto& ja = jv[jss::LimitAmount] =
2398 ja[jss::issuer] =
toBase58(account);
2400 jv[jss::TransactionType] = jss::TrustSet;
2401 jv[jss::Flags] = tfSetFreeze;
2409 auto tx = vault.deposit({.depositor = issuer, .id =
keylet.key, .amount = foo(20)});
2416 vault.withdraw({.depositor = issuer, .id =
keylet.key, .amount = foo(20)});
2421 env(vault.del({.owner = owner, .id = keylet.key}));
2435 testcase(
"IOU transfer fees not applied");
2437 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2441 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2448 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2449 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(100));
2452 auto tx = vault.clawback(
2453 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(50)});
2459 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2460 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(50));
2463 {.depositor = owner, .id = keylet.key, .amount = share(20'000'000)}));
2466 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2467 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(30));
2470 auto tx = vault.withdraw(
2471 {.depositor = owner, .id =
keylet.key, .amount = share(30'000'000)});
2472 tx[sfDestination] = charlie.human();
2477 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2478 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2479 BEAST_EXPECT(env.balance(vaultAccount(
keylet), issue) == asset(0));
2481 env(vault.del({.owner = owner, .id = keylet.key}));
2484 CaseArgs{.transferRate = 1.25});
2495 testcase(
"IOU no trust line to 3rd party");
2497 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2501 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2505 env.fund(XRP(1000), erin);
2511 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
2512 tx[sfDestination] = erin.human();
2527 testcase(
"IOU no trust line to depositor");
2529 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2534 env.trust(asset(0), owner);
2537 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2541 BEAST_EXPECT(trustline ==
nullptr);
2546 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
2564 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2571 env(trust(issuer, vaultAccount(
keylet)[
"IOU"], tfSetNoRipple));
2575 auto tx = vault.deposit(
2576 {.depositor = charlie, .id =
keylet.key, .amount = asset(100)});
2584 vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(100)});
2589 auto tx2 = vault.withdraw(
2590 {.depositor = owner, .id =
keylet.key, .amount = shares(100)});
2591 tx2[sfDestination] = charlie.human();
2598 tx[sfAccount] = charlie.human();
2599 tx[sfMPTokenIssuanceID] =
2601 tx[sfTransactionType] = jss::MPTokenAuthorize;
2615 env(pay(owner, charlie, shares(100)), Ter{
tecPATH_DRY});
2619 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(100)});
2624 env(vault.del({.owner = owner, .id = keylet.key}));
2626 {.charlieRipple =
false});
2634 auto const& vaultAccount,
2638 testcase(
"IOU calculation rounding");
2640 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2645 auto const startingOwnerBalance = env.balance(owner, asset);
2646 BEAST_EXPECT((startingOwnerBalance.value() ==
STAmount{asset, 11875, -2}));
2652 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2654 auto const tx1 = vault.deposit(
2655 {.depositor = owner, .id =
keylet.key, .amount = asset(
Number(375, -2))});
2656 for (
auto i = 0; i < 5; ++i)
2663 STAmount const xfer{asset, 1185, -1};
2664 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value() - xfer);
2665 BEAST_EXPECT(env.balance(vaultAccount(
keylet), asset) == xfer);
2667 auto const vault = env.le(
keylet);
2668 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
2669 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
2676 {.depositor = owner,
2678 .amount = asset(Number(1000 + (37 * 5), -1))}));
2681 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value());
2682 BEAST_EXPECT(env.balance(vaultAccount(
keylet), asset) == beast::kZero);
2683 auto const vault = env.le(
keylet);
2684 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::kZero);
2685 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::kZero);
2688 env(vault.del({.owner = owner, .id = keylet.key}));
2691 {.initialIOU =
Number(11875, -2)});
2694 Env
const env{*
this, testableAmendments()};
2696 env.current()->fees().accountReserve(0).drops() /
kDropsPerXrp.drops(),
2697 env.current()->fees().increment.drops() /
kDropsPerXrp.drops()};
2710 testcase(
"IOU no trust line to depositor no reserve");
2711 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2717 env.trust(asset(0), owner);
2720 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2724 BEAST_EXPECT(trustline ==
nullptr);
2726 env(ticket::create(owner, 1));
2730 tx = vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(10)});
2734 env(pay(charlie, owner, XRP(incReserve)));
2741 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});
2753 testcase(
"IOU no reserve for share MPToken");
2754 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
2758 env(pay(owner, charlie, asset(100)));
2761 env(ticket::create(charlie, 3));
2765 tx = vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(100)});
2769 env(pay(issuer, charlie, XRP(incReserve)));
2776 CaseArgs{.initialXRP = acctReserve + (incReserve * 4) + 1});
2786 Env env{*
this, testableAmendments()};
2787 Account const issuer{
"issuer"};
2789 Account const depositor{
"depositor"};
2790 Account const charlie{
"charlie"};
2791 Account const pdOwner{
"pdOwner"};
2792 Account const credIssuer1{
"credIssuer1"};
2793 Account const credIssuer2{
"credIssuer2"};
2795 Vault const vault{env};
2796 env.fund(XRP(1000), issuer, owner, depositor, charlie, pdOwner, credIssuer1, credIssuer2);
2798 env(fset(issuer, asfAllowTrustLineClawback));
2800 env.require(Flags(issuer, asfAllowTrustLineClawback));
2803 env.trust(asset(1000), owner);
2804 env(pay(issuer, owner, asset(500)));
2805 env.trust(asset(1000), depositor);
2806 env(pay(issuer, depositor, asset(500)));
2807 env.trust(asset(1000), charlie);
2808 env(pay(issuer, charlie, asset(5)));
2811 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
2814 BEAST_EXPECT(env.le(
keylet));
2817 testcase(
"private vault owner can deposit");
2818 auto tx = vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(50)});
2823 testcase(
"private vault depositor not authorized yet");
2825 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
2830 testcase(
"private vault cannot set non-existing domain");
2831 auto tx = vault.set({.owner = owner, .id =
keylet.key});
2837 testcase(
"private vault set domainId");
2840 pdomain::Credentials
const credentials1{
2841 {.issuer = credIssuer1, .credType = credType}};
2843 env(pdomain::setTx(pdOwner, credentials1));
2844 auto const domainId1 = [&]() {
2846 return pdomain::getNewDomain(env.meta());
2849 auto tx = vault.set({.owner = owner, .id =
keylet.key});
2861 {.issuer = credIssuer1, .credType = credType},
2862 {.issuer = credIssuer2, .credType = credType}};
2865 auto const domainId = [&]() {
2867 return pdomain::getNewDomain(env.meta());
2870 auto tx = vault.set({.owner = owner, .id =
keylet.key});
2876 tx = vault.set({.owner = owner, .id =
keylet.key});
2884 testcase(
"private vault depositor still not authorized");
2886 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
2891 auto const credKeylet = credentials::keylet(depositor, credIssuer1, credType);
2893 testcase(
"private vault depositor now authorized");
2894 env(credentials::create(depositor, credIssuer1, credType));
2895 env(credentials::accept(depositor, credIssuer1, credType));
2896 env(credentials::create(charlie, credIssuer1, credType));
2899 auto credSle = env.le(credKeylet);
2900 BEAST_EXPECT(credSle !=
nullptr);
2903 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
2907 tx = vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(50)});
2913 testcase(
"private vault depositor lost authorization");
2914 env(credentials::deleteCred(credIssuer1, depositor, credIssuer1, credType));
2915 env(credentials::deleteCred(credIssuer1, charlie, credIssuer1, credType));
2917 auto credSle = env.le(credKeylet);
2918 BEAST_EXPECT(credSle ==
nullptr);
2921 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
2927 auto const vault = env.le(
keylet);
2928 BEAST_EXPECT(vault !=
nullptr);
2929 return MPTIssue(vault->at(sfShareMPTID));
2933 testcase(
"private vault expired authorization");
2934 uint32_t
const closeTime =
2935 env.current()->header().parentCloseTime.time_since_epoch().count();
2937 auto tx0 = credentials::create(depositor, credIssuer2, credType);
2938 tx0[sfExpiration] = closeTime + 20;
2940 tx0 = credentials::create(charlie, credIssuer2, credType);
2941 tx0[sfExpiration] = closeTime + 20;
2945 env(credentials::accept(depositor, credIssuer2, credType));
2946 env(credentials::accept(charlie, credIssuer2, credType));
2952 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
2956 auto const tokenKeylet =
2958 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
2967 auto const credsKeylet = credentials::keylet(depositor, credIssuer2, credType);
2968 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
2971 vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(1)});
2975 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
2979 auto const credsKeylet = credentials::keylet(charlie, credIssuer2, credType);
2980 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
2981 auto const tokenKeylet =
2983 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
2986 vault.deposit({.depositor = charlie, .id =
keylet.key, .amount = asset(2)});
2990 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
2991 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
2996 testcase(
"private vault reset domainId");
2997 auto tx = vault.set({.owner = owner, .id =
keylet.key});
2998 tx[sfDomainID] =
"0";
3002 tx = vault.deposit({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
3006 tx = vault.withdraw({.depositor = depositor, .id =
keylet.key, .amount = asset(50)});
3010 tx = vault.clawback(
3011 {.issuer = issuer, .id =
keylet.key, .holder = depositor, .amount = asset(0)});
3014 tx = vault.clawback(
3015 {.issuer = issuer, .id =
keylet.key, .holder = owner, .amount = asset(0)});
3172 auto testCase = [&,
this](
3174 Env env{*
this, testableAmendments()};
3176 Account const issuer{
"issuer"};
3177 Account const depositor{
"depositor"};
3179 env.fund(XRP(1000), issuer, owner, depositor);
3180 env(fset(issuer, asfAllowTrustLineClawback));
3184 env.trust(asset(1000), owner);
3185 env.trust(asset(1000), depositor);
3186 env(pay(issuer, owner, asset(200)));
3187 env(pay(issuer, depositor, asset(200)));
3190 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
3191 tx[sfScale] =
scale;
3194 auto const [vaultAccount, issuanceId] =
3196 auto const vault = env.le(
keylet);
3197 return {
Account(
"vault", vault->at(sfAccount)), vault->at(sfShareMPTID)};
3200 env.memoize(vaultAccount);
3203 return env.app().getOpenLedger().modify(
3207 if (!BEAST_EXPECT(vault))
3210 if (!BEAST_EXPECT(shares))
3212 if (fn(*vault, *shares))
3227 .depositor = depositor,
3228 .vaultAccount = vaultAccount,
3238 testCase(18, [&,
this](Env& env, Data d) {
3239 testcase(
"Scale deposit overflow on first deposit");
3240 auto tx = d.vault.deposit(
3241 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
3246 testCase(18, [&,
this](Env& env, Data d) {
3247 testcase(
"Scale deposit overflow on second deposit");
3250 auto tx = d.vault.deposit(
3251 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3257 auto tx = d.vault.deposit(
3258 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
3264 testCase(18, [&,
this](Env& env, Data d) {
3265 testcase(
"Scale deposit overflow on total shares");
3268 auto tx = d.vault.deposit(
3269 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3275 auto tx = d.vault.deposit(
3276 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3282 testCase(1, [&,
this](Env& env, Data d) {
3285 auto const start = env.balance(d.depositor, d.assets).number();
3286 auto tx = d.vault.deposit(
3287 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(1)});
3290 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3291 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start - 1));
3294 testCase(1, [&,
this](Env& env, Data d) {
3295 testcase(
"Scale deposit insignificant amount");
3297 auto tx = d.vault.deposit(
3298 {.depositor = d.depositor,
3304 testCase(1, [&,
this](Env& env, Data d) {
3305 testcase(
"Scale deposit exact, using full precision");
3307 auto const start = env.balance(d.depositor, d.assets).number();
3308 auto tx = d.vault.deposit(
3309 {.depositor = d.depositor,
3314 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3316 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(15, -1)));
3319 testCase(1, [&,
this](Env& env, Data d) {
3320 testcase(
"Scale deposit exact, truncating from .5");
3322 auto const start = env.balance(d.depositor, d.assets).number();
3326 auto tx = d.vault.deposit(
3327 {.depositor = d.depositor,
3332 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3334 env.balance(d.depositor, d.assets) ==
3339 auto tx = d.vault.deposit(
3340 {.depositor = d.depositor,
3345 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3347 env.balance(d.depositor, d.assets) ==
3352 auto tx = d.vault.deposit(
3353 {.depositor = d.depositor,
3358 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3360 env.balance(d.depositor, d.assets) ==
3365 testCase(1, [&,
this](Env& env, Data d) {
3366 testcase(
"Scale deposit exact, truncating from .01");
3368 auto const start = env.balance(d.depositor, d.assets).number();
3370 auto tx = d.vault.deposit(
3371 {.depositor = d.depositor,
3376 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3378 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(12, -1)));
3382 auto tx = d.vault.deposit(
3383 {.depositor = d.depositor,
3388 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3390 env.balance(d.depositor, d.assets) ==
3395 testCase(1, [&,
this](Env& env, Data d) {
3396 testcase(
"Scale deposit exact, truncating from .99");
3398 auto const start = env.balance(d.depositor, d.assets).number();
3400 auto tx = d.vault.deposit(
3401 {.depositor = d.depositor,
3406 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3408 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(12, -1)));
3412 auto tx = d.vault.deposit(
3413 {.depositor = d.depositor,
3418 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3420 env.balance(d.depositor, d.assets) ==
3425 testCase(1, [&,
this](Env& env, Data d) {
3427 auto const start = env.balance(d.depositor, d.assets).number();
3428 auto tx = d.vault.deposit(
3429 {.depositor = d.depositor,
3434 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3436 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3438 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3440 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-1000, 0)));
3448 auto const start = env.balance(d.depositor, d.assets).number();
3449 auto tx = d.vault.withdraw(
3450 {.depositor = d.depositor,
3455 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3457 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(10, 0)));
3459 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3461 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-900, 0)));
3465 testcase(
"Scale redeem with rounding");
3470 auto const start = env.balance(d.depositor, d.assets).number();
3471 d.peek([](
SLE& vault,
auto&) ->
bool {
3472 vault[sfAssetsAvailable] =
Number(1);
3480 auto tx = d.vault.withdraw(
3481 {.depositor = d.depositor,
3486 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3488 env.balance(d.depositor, d.assets) ==
3491 env.balance(d.vaultAccount, d.assets) ==
3494 env.balance(d.vaultAccount, d.shares) ==
3504 auto const start = env.balance(d.depositor, d.assets).number();
3506 tx = d.vault.withdraw(
3507 {.depositor = d.depositor,
3512 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 21));
3514 env.balance(d.depositor, d.assets) ==
3517 env.balance(d.vaultAccount, d.assets) ==
3520 env.balance(d.vaultAccount, d.shares) ==
3526 auto const rest = env.balance(d.depositor, d.shares).number();
3528 tx = d.vault.withdraw(
3529 {.depositor = d.depositor,
3531 .amount =
STAmount(d.share, rest)});
3534 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3535 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3536 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3540 testCase(18, [&,
this](Env& env, Data d) {
3541 testcase(
"Scale withdraw overflow");
3544 auto tx = d.vault.deposit(
3545 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3551 auto tx = d.vault.withdraw(
3552 {.depositor = d.depositor,
3560 testCase(1, [&,
this](Env& env, Data d) {
3562 auto const start = env.balance(d.depositor, d.assets).number();
3563 auto tx = d.vault.deposit(
3564 {.depositor = d.depositor,
3569 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3571 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3573 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3575 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-1000, 0)));
3586 auto const start = env.balance(d.depositor, d.assets).number();
3587 auto tx = d.vault.withdraw(
3588 {.depositor = d.depositor,
3593 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3595 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(10, 0)));
3597 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3599 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share,
Number(-900, 0)));
3603 testcase(
"Scale withdraw insignificant amount");
3604 auto tx = d.vault.withdraw(
3605 {.depositor = d.depositor,
3612 testcase(
"Scale withdraw with rounding assets");
3620 auto const start = env.balance(d.depositor, d.assets).number();
3621 d.peek([](
SLE& vault,
auto&) ->
bool {
3622 vault[sfAssetsAvailable] =
Number(1);
3630 auto tx = d.vault.withdraw(
3631 {.depositor = d.depositor,
3636 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3638 env.balance(d.depositor, d.assets) ==
3641 env.balance(d.vaultAccount, d.assets) ==
3644 env.balance(d.vaultAccount, d.shares) ==
3649 testcase(
"Scale withdraw with rounding shares up");
3657 auto const start = env.balance(d.depositor, d.assets).number();
3658 auto tx = d.vault.withdraw(
3659 {.depositor = d.depositor,
3664 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
3666 env.balance(d.depositor, d.assets) ==
3669 env.balance(d.vaultAccount, d.assets) ==
3672 env.balance(d.vaultAccount, d.shares) ==
3677 testcase(
"Scale withdraw with rounding shares down");
3685 auto const start = env.balance(d.depositor, d.assets).number();
3686 auto tx = d.vault.withdraw(
3687 {.depositor = d.depositor,
3692 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
3694 env.balance(d.depositor, d.assets) ==
3697 env.balance(d.vaultAccount, d.assets) ==
3700 env.balance(d.vaultAccount, d.shares) ==
3705 testcase(
"Scale withdraw tiny amount");
3707 auto const start = env.balance(d.depositor, d.assets).number();
3708 auto tx = d.vault.withdraw(
3709 {.depositor = d.depositor,
3714 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
3716 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start +
Number(1, -1)));
3718 env.balance(d.vaultAccount, d.assets) ==
3721 env.balance(d.vaultAccount, d.shares) ==
3727 auto const rest = env.balance(d.vaultAccount, d.assets).number();
3729 tx = d.vault.withdraw(
3730 {.depositor = d.depositor,
3732 .amount =
STAmount(d.asset, rest)});
3735 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3736 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3737 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3741 testCase(18, [&,
this](Env& env, Data d) {
3742 testcase(
"Scale clawback overflow");
3745 auto tx = d.vault.deposit(
3746 {.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
3752 auto tx = d.vault.clawback(
3753 {.issuer = d.issuer,
3755 .holder = d.depositor,
3762 testCase(1, [&,
this](Env& env, Data d) {
3764 auto const start = env.balance(d.depositor, d.assets).number();
3765 auto tx = d.vault.deposit(
3766 {.depositor = d.depositor,
3771 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3773 env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start -
Number(100, 0)));
3775 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(100, 0)));
3777 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(1000, 0)));
3787 auto const start = env.balance(d.depositor, d.assets).number();
3788 auto tx = d.vault.clawback(
3789 {.issuer = d.issuer,
3791 .holder = d.depositor,
3795 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
3796 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3798 env.balance(d.vaultAccount, d.assets) ==
STAmount(d.asset,
Number(90, 0)));
3800 env.balance(d.vaultAccount, d.shares) ==
STAmount(d.share, -
Number(900, 0)));
3804 testcase(
"Scale clawback insignificant amount");
3805 auto tx = d.vault.clawback(
3806 {.issuer = d.issuer,
3808 .holder = d.depositor,
3814 testcase(
"Scale clawback with rounding assets");
3822 auto const start = env.balance(d.depositor, d.assets).number();
3823 auto tx = d.vault.clawback(
3824 {.issuer = d.issuer,
3826 .holder = d.depositor,
3830 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
3831 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3833 env.balance(d.vaultAccount, d.assets) ==
3836 env.balance(d.vaultAccount, d.shares) ==
3841 testcase(
"Scale clawback with rounding shares up");
3849 auto const start = env.balance(d.depositor, d.assets).number();
3850 auto tx = d.vault.clawback(
3851 {.issuer = d.issuer,
3853 .holder = d.depositor,
3857 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
3858 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3860 env.balance(d.vaultAccount, d.assets) ==
3863 env.balance(d.vaultAccount, d.shares) ==
3868 testcase(
"Scale clawback with rounding shares down");
3876 auto const start = env.balance(d.depositor, d.assets).number();
3877 auto tx = d.vault.clawback(
3878 {.issuer = d.issuer,
3880 .holder = d.depositor,
3884 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
3885 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3887 env.balance(d.vaultAccount, d.assets) ==
3890 env.balance(d.vaultAccount, d.shares) ==
3895 testcase(
"Scale clawback tiny amount");
3897 auto const start = env.balance(d.depositor, d.assets).number();
3898 auto tx = d.vault.clawback(
3899 {.issuer = d.issuer,
3901 .holder = d.depositor,
3905 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
3906 BEAST_EXPECT(env.balance(d.depositor, d.assets) ==
STAmount(d.asset, start));
3908 env.balance(d.vaultAccount, d.assets) ==
3911 env.balance(d.vaultAccount, d.shares) ==
3917 auto const rest = env.balance(d.vaultAccount, d.assets).number();
3918 d.peek([](
SLE& vault,
auto&) ->
bool {
3919 vault[sfAssetsAvailable] =
Number(5);
3927 tx = d.vault.clawback(
3928 {.issuer = d.issuer,
3930 .holder = d.depositor,
3931 .amount =
STAmount(d.asset, rest)});
3934 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3935 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
3936 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
3943 testCase(1, [&,
this](Env& env, Data d) {
3944 using namespace loanBroker;
3945 using namespace loan;
3947 testcase(
"Scale clawback clamped with outstanding loan");
3949 auto tx = d.vault.deposit(
3950 {.depositor = d.depositor,
3955 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3959 env(
set(d.owner, d.keylet.key));
3966 kPaymentInterval(120),
3968 Sig(sfCounterpartySignature, d.owner),
3969 Fee(env.current()->fees().base * 2),
3974 auto const sle = env.le(d.keylet);
3975 BEAST_EXPECT(sle->at(sfAssetsAvailable) ==
STAmount(d.asset,
Number(60, 0)));
3976 BEAST_EXPECT(sle->at(sfAssetsTotal) ==
STAmount(d.asset,
Number(100, 0)));
3981 tx = d.vault.clawback(
3982 {.issuer = d.issuer,
3984 .holder = d.depositor,
3990 auto const sle = env.le(d.keylet);
3991 BEAST_EXPECT(sle !=
nullptr);
3992 BEAST_EXPECT(sle->at(sfAssetsAvailable) ==
STAmount(d.asset,
Number(0, 0)));
3993 BEAST_EXPECT(sle->at(sfAssetsTotal) ==
STAmount(d.asset,
Number(40, 0)));
3996 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(400));
4007 Env env{*
this, testableAmendments()};
4009 Account const issuer{
"issuer"};
4010 Vault const vault{env};
4011 env.fund(XRP(1000), issuer, owner);
4015 env.trust(asset(1000), owner);
4016 env(pay(issuer, owner, asset(200)));
4019 auto const sequence = env.seq(owner);
4020 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
4026 auto tx1 = vault.deposit({.depositor = owner, .id =
keylet.key, .amount = asset(50)});
4029 auto tx2 = vault.set({.owner = owner, .id =
keylet.key});
4030 tx2[sfAssetsMaximum] = asset(1000).number();
4035 auto const sleVault = [&env,
keylet =
keylet,
this]() {
4036 auto const vault = env.le(
keylet);
4037 BEAST_EXPECT(vault !=
nullptr);
4041 auto const check = [&,
keylet =
keylet, sle = sleVault,
this](
4044 BEAST_EXPECT(vault.isObject());
4046 static constexpr auto kCheckString =
4048 return node.isMember(field.fieldName) && node[field.fieldName].isString() &&
4049 node[field.fieldName] == v;
4051 static constexpr auto kCheckObject =
4053 return node.isMember(field.fieldName) && node[field.fieldName].isObject() &&
4054 node[field.fieldName] == v;
4056 static constexpr auto kCheckInt = [](
auto& node,
SField const& field,
int v) ->
bool {
4057 return node.isMember(field.fieldName) &&
4058 ((node[field.fieldName].isInt() && node[field.fieldName] ==
json::Int(v)) ||
4059 (node[field.fieldName].isUInt() && node[field.fieldName] ==
json::UInt(v)));
4062 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4063 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(
keylet.key));
4064 BEAST_EXPECT(kCheckInt(vault, sfFlags, 0));
4067 BEAST_EXPECT(kCheckString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4068 BEAST_EXPECT(kCheckObject(vault, sfAsset,
toJson(sle->at(sfAsset))));
4069 BEAST_EXPECT(kCheckString(vault, sfAssetsAvailable,
"50"));
4070 BEAST_EXPECT(kCheckString(vault, sfAssetsMaximum,
"1000"));
4071 BEAST_EXPECT(kCheckString(vault, sfAssetsTotal,
"50"));
4072 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
4074 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4075 BEAST_EXPECT(kCheckString(vault, sfShareMPTID, strShareID));
4076 BEAST_EXPECT(kCheckString(vault, sfOwner,
toBase58(owner.id())));
4077 BEAST_EXPECT(kCheckInt(vault, sfSequence, sequence));
4080 if (issuance.isObject())
4082 BEAST_EXPECT(issuance[
"LedgerEntryType"].asString() ==
"MPTokenIssuance");
4083 BEAST_EXPECT(issuance[jss::mpt_issuance_id].asString() == strShareID);
4084 BEAST_EXPECT(kCheckInt(issuance, sfSequence, 1));
4085 BEAST_EXPECT(kCheckInt(
4086 issuance, sfFlags,
int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer)));
4087 BEAST_EXPECT(kCheckString(issuance, sfOutstandingAmount,
"50000000"));
4092 testcase(
"RPC ledger_entry selected by key");
4094 jvParams[jss::ledger_index] = jss::validated;
4096 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4098 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4099 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4100 check(jvVault[jss::result][jss::node]);
4104 testcase(
"RPC ledger_entry selected by owner and seq");
4106 jvParams[jss::ledger_index] = jss::validated;
4107 jvParams[jss::vault][jss::owner] = owner.human();
4108 jvParams[jss::vault][jss::seq] = sequence;
4109 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4111 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4112 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4113 check(jvVault[jss::result][jss::node]);
4117 testcase(
"RPC ledger_entry cannot find vault by key");
4119 jvParams[jss::ledger_index] = jss::validated;
4121 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4122 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4126 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4128 jvParams[jss::ledger_index] = jss::validated;
4129 jvParams[jss::vault][jss::owner] = issuer.human();
4130 jvParams[jss::vault][jss::seq] = 1'000'000;
4131 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4132 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4136 testcase(
"RPC ledger_entry malformed key");
4138 jvParams[jss::ledger_index] = jss::validated;
4139 jvParams[jss::vault] = 42;
4140 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4141 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4145 testcase(
"RPC ledger_entry malformed owner");
4147 jvParams[jss::ledger_index] = jss::validated;
4148 jvParams[jss::vault][jss::owner] = 42;
4149 jvParams[jss::vault][jss::seq] = sequence;
4150 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4151 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedOwner");
4155 testcase(
"RPC ledger_entry malformed seq");
4157 jvParams[jss::ledger_index] = jss::validated;
4158 jvParams[jss::vault][jss::owner] = issuer.human();
4159 jvParams[jss::vault][jss::seq] =
"foo";
4160 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4161 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4165 testcase(
"RPC ledger_entry negative seq");
4167 jvParams[jss::ledger_index] = jss::validated;
4168 jvParams[jss::vault][jss::owner] = issuer.human();
4169 jvParams[jss::vault][jss::seq] = -1;
4170 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4171 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4175 testcase(
"RPC ledger_entry oversized seq");
4177 jvParams[jss::ledger_index] = jss::validated;
4178 jvParams[jss::vault][jss::owner] = issuer.human();
4179 jvParams[jss::vault][jss::seq] = 1e20;
4180 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4181 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4185 testcase(
"RPC ledger_entry bool seq");
4187 jvParams[jss::ledger_index] = jss::validated;
4188 jvParams[jss::vault][jss::owner] = issuer.human();
4189 jvParams[jss::vault][jss::seq] =
true;
4190 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4191 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() ==
"malformedRequest");
4198 jvParams[jss::account] = owner.human();
4199 jvParams[jss::type] = jss::vault;
4200 auto jv = env.rpc(
"json",
"account_objects",
to_string(jvParams))[jss::result];
4202 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4203 check(jv[jss::account_objects][0u]);
4210 jvParams[jss::ledger_index] = jss::validated;
4211 jvParams[jss::binary] =
false;
4212 jvParams[jss::type] = jss::vault;
4214 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4215 check(jv[jss::result][jss::state][0u]);
4219 testcase(
"RPC vault_info command line");
4222 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4223 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4224 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
4230 jvParams[jss::ledger_index] = jss::validated;
4232 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4234 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4235 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4236 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
4240 testcase(
"RPC vault_info invalid vault_id");
4242 jvParams[jss::ledger_index] = jss::validated;
4243 jvParams[jss::vault_id] =
"foobar";
4244 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4245 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4249 testcase(
"RPC vault_info json invalid index");
4251 jvParams[jss::ledger_index] = jss::validated;
4252 jvParams[jss::vault_id] = 0;
4253 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4254 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4258 testcase(
"RPC vault_info json by owner and sequence");
4260 jvParams[jss::ledger_index] = jss::validated;
4261 jvParams[jss::owner] = owner.human();
4262 jvParams[jss::seq] = sequence;
4263 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4265 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4266 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4267 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
4271 testcase(
"RPC vault_info json malformed sequence");
4273 jvParams[jss::ledger_index] = jss::validated;
4274 jvParams[jss::owner] = owner.human();
4275 jvParams[jss::seq] =
"foobar";
4276 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4277 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4281 testcase(
"RPC vault_info json invalid sequence");
4283 jvParams[jss::ledger_index] = jss::validated;
4284 jvParams[jss::owner] = owner.human();
4285 jvParams[jss::seq] = 0;
4286 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4287 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4291 testcase(
"RPC vault_info json negative sequence");
4293 jvParams[jss::ledger_index] = jss::validated;
4294 jvParams[jss::owner] = owner.human();
4295 jvParams[jss::seq] = -1;
4296 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4297 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4301 testcase(
"RPC vault_info json oversized sequence");
4303 jvParams[jss::ledger_index] = jss::validated;
4304 jvParams[jss::owner] = owner.human();
4305 jvParams[jss::seq] = 1e20;
4306 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4307 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4311 testcase(
"RPC vault_info json bool sequence");
4313 jvParams[jss::ledger_index] = jss::validated;
4314 jvParams[jss::owner] = owner.human();
4315 jvParams[jss::seq] =
true;
4316 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4317 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4321 testcase(
"RPC vault_info json malformed owner");
4323 jvParams[jss::ledger_index] = jss::validated;
4324 jvParams[jss::owner] =
"foobar";
4325 jvParams[jss::seq] = sequence;
4326 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4327 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4331 testcase(
"RPC vault_info json invalid combination only owner");
4333 jvParams[jss::ledger_index] = jss::validated;
4334 jvParams[jss::owner] = owner.human();
4335 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4336 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4340 testcase(
"RPC vault_info json invalid combination only seq");
4342 jvParams[jss::ledger_index] = jss::validated;
4343 jvParams[jss::seq] = sequence;
4344 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4345 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4349 testcase(
"RPC vault_info json invalid combination seq vault_id");
4351 jvParams[jss::ledger_index] = jss::validated;
4353 jvParams[jss::seq] = sequence;
4354 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4355 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4359 testcase(
"RPC vault_info json invalid combination owner vault_id");
4361 jvParams[jss::ledger_index] = jss::validated;
4363 jvParams[jss::owner] = owner.human();
4364 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4365 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4370 "RPC vault_info json invalid combination owner seq "
4373 jvParams[jss::ledger_index] = jss::validated;
4375 jvParams[jss::seq] = sequence;
4376 jvParams[jss::owner] = owner.human();
4377 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4378 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4382 testcase(
"RPC vault_info json no input");
4384 jvParams[jss::ledger_index] = jss::validated;
4385 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4386 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4390 testcase(
"RPC vault_info command line invalid index");
4391 json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4392 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4396 testcase(
"RPC vault_info command line invalid index");
4397 json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4398 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"malformedRequest");
4402 testcase(
"RPC vault_info command line invalid index");
4404 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"entryNotFound");
4408 testcase(
"RPC vault_info command line invalid ledger");
4410 BEAST_EXPECT(jv[jss::result][jss::error].asString() ==
"lgrNotFound");
4418 using namespace loanBroker;
4419 using namespace loan;
4422 auto const vaultAssetBalance = [&](
Keylet const& vaultKeylet) {
4423 auto const sleVault = env.le(vaultKeylet);
4424 BEAST_EXPECT(sleVault !=
nullptr);
4426 return std::make_pair(sleVault->at(sfAssetsAvailable), sleVault->at(sfAssetsTotal));
4429 auto const vaultShareBalance = [&](
Keylet const& vaultKeylet) {
4430 auto const sleVault = env.le(vaultKeylet);
4431 BEAST_EXPECT(sleVault !=
nullptr);
4434 BEAST_EXPECT(sleIssuance !=
nullptr);
4436 return sleIssuance->at(sfOutstandingAmount);
4439 auto const setupVault = [&](
PrettyAsset const& asset,
4442 Vault const vault{env};
4444 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
4448 auto const& vaultSle = env.le(vaultKeylet);
4449 BEAST_EXPECT(vaultSle !=
nullptr);
4451 Asset const share = vaultSle->at(sfShareMPTID);
4454 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
4458 auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet);
4459 BEAST_EXPECT(availablePreDefault == totalPreDefault);
4460 BEAST_EXPECT(availablePreDefault == asset(100).value());
4465 .id = vaultKeylet.key,
4466 .holder = depositor,
4467 .amount = share(0).value()}),
4471 auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
4474 env(
set(owner, vaultKeylet.key));
4477 auto const& loanKeylet =
keylet::loan(brokerKeylet.key, 1);
4480 env(
set(depositor, brokerKeylet.key, asset(100).value()),
4483 kPaymentInterval(120),
4485 Sig(sfCounterpartySignature, owner),
4486 Fee(env.current()->fees().base * 2),
4494 .id = vaultKeylet.key,
4495 .holder = depositor,
4496 .amount = share(0).value()}),
4502 env(manage(owner, loanKeylet.key, tfLoanDefault), Ter(
tesSUCCESS));
4504 auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet);
4506 BEAST_EXPECT(availablePostDefault == totalPostDefault);
4507 BEAST_EXPECT(availablePostDefault == asset(0).value());
4508 BEAST_EXPECT(vaultShareBalance(vaultKeylet) == sharesAvailable);
4513 auto const testCase = [&](
PrettyAsset const& asset,
4518 testcase(
"VaultClawback (share) - " + prefix +
" owner asset clawback fails");
4519 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4523 auto const expectedTer = [&]() {
4530 env(vault.clawback({
4532 .id = vaultKeylet.key,
4533 .holder = depositor,
4534 .amount = asset(100).value(),
4542 "VaultClawback (share) - " + prefix +
" owner incomplete share clawback fails");
4543 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4544 auto const& vaultSle = env.le(vaultKeylet);
4545 if (!BEAST_EXPECT(vaultSle))
4547 Asset const share = vaultSle->at(sfShareMPTID);
4548 env(vault.clawback({
4550 .id = vaultKeylet.key,
4551 .holder = depositor,
4552 .amount = share(1).value(),
4560 "VaultClawback (share) - " + prefix +
4561 " owner implicit complete share clawback");
4562 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4563 env(vault.clawback({
4565 .id = vaultKeylet.key,
4566 .holder = depositor,
4576 "VaultClawback (share) - " + prefix +
4577 " owner explicit complete share clawback succeeds");
4578 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
4579 auto const& vaultSle = env.le(vaultKeylet);
4580 if (!BEAST_EXPECT(vaultSle))
4582 Asset const share = vaultSle->at(sfShareMPTID);
4583 env(vault.clawback({
4585 .id = vaultKeylet.key,
4586 .holder = depositor,
4587 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4593 testcase(
"VaultClawback (share) - " + prefix +
" owner can clawback own shares");
4594 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
4595 auto const& vaultSle = env.le(vaultKeylet);
4596 if (!BEAST_EXPECT(vaultSle))
4598 Asset const share = vaultSle->at(sfShareMPTID);
4599 env(vault.clawback({
4601 .id = vaultKeylet.key,
4603 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4610 testcase(
"VaultClawback (share) - " + prefix +
" empty vault share clawback fails");
4611 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
4612 auto const& vaultSle = env.le(vaultKeylet);
4613 if (!BEAST_EXPECT(vaultSle))
4615 Asset const share = vaultSle->at(sfShareMPTID);
4616 env(vault.clawback({
4618 .id = vaultKeylet.key,
4620 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4625 env(vault.clawback({
4627 .id = vaultKeylet.key,
4629 .amount = share(vaultShareBalance(vaultKeylet)).value(),
4637 Account const depositor{
"bob"};
4638 Account const issuer{
"issuer"};
4640 env.fund(XRP(10000), issuer, owner, depositor);
4645 testCase(xrp,
"XRP", owner, depositor);
4646 testCase(xrp,
"XRP (depositor is owner)", owner, owner);
4650 env(fset(issuer, asfAllowTrustLineClawback));
4653 env.trust(iou(1000), owner);
4654 env.trust(iou(1000), depositor);
4655 env(pay(issuer, owner, iou(100)));
4656 env(pay(issuer, depositor, iou(100)));
4658 testCase(iou,
"IOU", owner, depositor);
4659 testCase(iou,
"IOU (owner is issuer)", issuer, depositor);
4662 MPTTester mptt{env, issuer, kMptInitNoFund};
4663 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
4665 mptt.authorize({.account = owner});
4666 mptt.authorize({.account = depositor});
4667 env(pay(issuer, owner, mpt(1000)));
4668 env(pay(issuer, depositor, mpt(1000)));
4670 testCase(mpt,
"MPT", owner, depositor);
4671 testCase(mpt,
"MPT (owner is issuer)", issuer, depositor);
4678 using namespace loanBroker;
4679 using namespace loan;
4681 env.enableFeature(fixCleanup3_1_3);
4683 auto const setupVault = [&](
PrettyAsset const& asset,
4687 Vault const vault{env};
4689 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
4693 auto const& vaultSle = env.le(vaultKeylet);
4694 BEAST_EXPECT(vaultSle !=
nullptr);
4695 env.memoize(
Account(
"vault", vaultSle->at(sfAccount)));
4697 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
4704 auto const testCase = [&](
PrettyAsset const& asset,
4711 testcase(
"VaultClawback (asset) - " + prefix +
" issuer XRP clawback fails");
4712 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4715 env(vault.clawback({
4717 .id = vaultKeylet.key,
4719 .amount = asset(1).value(),
4723 env(vault.clawback({
4725 .id = vaultKeylet.key,
4734 "VaultClawback (asset) - " + prefix +
" clawback for different asset fails");
4735 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4737 Account const issuer2{
"issuer2"};
4739 env(vault.clawback({
4741 .id = vaultKeylet.key,
4742 .holder = depositor,
4743 .amount = asset2(1).value(),
4750 "VaultClawback (asset) - " + prefix +
4751 " ambiguous owner/issuer asset clawback fails");
4752 auto [vault, vaultKeylet] = setupVault(asset, issuer, depositor, issuer);
4753 env(vault.clawback({
4755 .id = vaultKeylet.key,
4762 testcase(
"VaultClawback (asset) - " + prefix +
" non-issuer asset clawback fails");
4763 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4765 env(vault.clawback({
4767 .id = vaultKeylet.key,
4768 .holder = depositor,
4772 env(vault.clawback({
4774 .id = vaultKeylet.key,
4775 .holder = depositor,
4776 .amount = asset(1).value(),
4782 testcase(
"VaultClawback (asset) - " + prefix +
" issuer clawback from self fails");
4783 auto [vault, vaultKeylet] = setupVault(asset, owner, issuer, issuer);
4784 env(vault.clawback({
4786 .id = vaultKeylet.key,
4793 testcase(
"VaultClawback (asset) - " + prefix +
" issuer share clawback fails");
4794 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4795 auto const& vaultSle = env.le(vaultKeylet);
4796 if (!BEAST_EXPECT(vaultSle))
4798 Asset const share = vaultSle->at(sfShareMPTID);
4800 env(vault.clawback({
4802 .id = vaultKeylet.key,
4803 .holder = depositor,
4804 .amount = share(1).value(),
4811 "VaultClawback (asset) - " + prefix +
4812 " partial issuer asset clawback succeeds");
4813 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4815 env(vault.clawback({
4817 .id = vaultKeylet.key,
4818 .holder = depositor,
4819 .amount = asset(1).value(),
4826 "VaultClawback (asset) - " + prefix +
" full issuer asset clawback succeeds");
4827 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4829 env(vault.clawback({
4831 .id = vaultKeylet.key,
4832 .holder = depositor,
4833 .amount = asset(100).value(),
4840 "VaultClawback (asset) - " + prefix +
4841 " implicit full issuer asset clawback succeeds");
4842 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4844 env(vault.clawback({
4846 .id = vaultKeylet.key,
4847 .holder = depositor,
4854 "VaultClawback (asset) - " + prefix +
4855 " zero-amount clawback clamped with outstanding loan");
4856 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4858 auto const vaultSle = env.le(vaultKeylet);
4859 if (!BEAST_EXPECT(vaultSle))
4866 env(
set(owner, vaultKeylet.key));
4871 env(
set(depositor, brokerKeylet.key, asset(40).value()),
4874 kPaymentInterval(120),
4876 Sig(sfCounterpartySignature, owner),
4877 Fee(env.current()->fees().base * 2),
4882 auto const sle = env.le(vaultKeylet);
4883 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
4884 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
4890 env(vault.clawback({
4892 .id = vaultKeylet.key,
4893 .holder = depositor,
4900 auto const sle = env.le(vaultKeylet);
4901 BEAST_EXPECT(sle !=
nullptr);
4902 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
4903 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
4906 auto const sharesAfter = env.balance(depositor, shares);
4907 BEAST_EXPECT(sharesAfter == shares(
Number{4, sle->at(sfScale) + 1}));
4913 "VaultClawback (asset) - " + prefix +
4914 " non-zero clawback clamped with outstanding loan");
4915 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4917 auto const vaultSle = env.le(vaultKeylet);
4918 if (!BEAST_EXPECT(vaultSle))
4924 env(
set(owner, vaultKeylet.key));
4928 env(
set(depositor, brokerKeylet.key, asset(40).value()),
4931 kPaymentInterval(120),
4933 Sig(sfCounterpartySignature, owner),
4934 Fee(env.current()->fees().base * 2),
4939 auto const sle = env.le(vaultKeylet);
4940 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
4941 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
4945 env(vault.clawback({
4947 .id = vaultKeylet.key,
4948 .holder = depositor,
4949 .amount = asset(100).value(),
4955 auto const sle = env.le(vaultKeylet);
4956 BEAST_EXPECT(sle !=
nullptr);
4957 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
4958 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
4961 auto const sharesAfter = env.balance(depositor, shares);
4962 BEAST_EXPECT(sharesAfter == shares(
Number{4, sle->at(sfScale) + 1}));
4968 "VaultClawback (asset) - " + prefix +
4969 " partial clawback below available with outstanding loan");
4970 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
4972 auto const vaultSle = env.le(vaultKeylet);
4973 if (!BEAST_EXPECT(vaultSle))
4979 env(
set(owner, vaultKeylet.key));
4983 env(
set(depositor, brokerKeylet.key, asset(40).value()),
4986 kPaymentInterval(120),
4988 Sig(sfCounterpartySignature, owner),
4989 Fee(env.current()->fees().base * 2),
4994 auto const sle = env.le(vaultKeylet);
4995 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
4996 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
5000 env(vault.clawback({
5002 .id = vaultKeylet.key,
5003 .holder = depositor,
5004 .amount = asset(30).value(),
5010 auto const sle = env.le(vaultKeylet);
5011 BEAST_EXPECT(sle !=
nullptr);
5012 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(30).value());
5013 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(70).value());
5016 auto const sharesAfter = env.balance(depositor, shares);
5017 BEAST_EXPECT(sharesAfter == shares(
Number{7, sle->at(sfScale) + 1}));
5023 "VaultClawback (asset) - " + prefix +
5024 " clawback exactly equal to available with outstanding loan");
5025 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
5027 auto const vaultSle = env.le(vaultKeylet);
5028 if (!BEAST_EXPECT(vaultSle))
5033 env(
set(owner, vaultKeylet.key));
5037 env(
set(depositor, brokerKeylet.key, asset(40).value()),
5040 kPaymentInterval(120),
5042 Sig(sfCounterpartySignature, owner),
5043 Fee(env.current()->fees().base * 2),
5048 env(vault.clawback({
5050 .id = vaultKeylet.key,
5051 .holder = depositor,
5052 .amount = asset(60).value(),
5058 auto const sle = env.le(vaultKeylet);
5059 BEAST_EXPECT(sle !=
nullptr);
5060 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
5061 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
5064 auto const sharesAfter = env.balance(depositor, shares);
5065 BEAST_EXPECT(sharesAfter == shares(
Number{4, sle->at(sfScale) + 1}));
5071 "VaultClawback (asset) - " + prefix +
5072 " clawback with zero available (fully borrowed)");
5073 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
5075 auto const vaultSle = env.le(vaultKeylet);
5076 if (!BEAST_EXPECT(vaultSle))
5081 env(
set(owner, vaultKeylet.key));
5085 env(
set(depositor, brokerKeylet.key, asset(100).value()),
5088 kPaymentInterval(120),
5090 Sig(sfCounterpartySignature, owner),
5091 Fee(env.current()->fees().base * 2),
5096 auto const sle = env.le(vaultKeylet);
5097 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
5098 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
5101 auto const sharesBefore = env.balance(depositor, shares);
5105 env(vault.clawback({
5107 .id = vaultKeylet.key,
5108 .holder = depositor,
5114 env(vault.clawback({
5116 .id = vaultKeylet.key,
5117 .holder = depositor,
5118 .amount = asset(50).value(),
5125 auto const sle = env.le(vaultKeylet);
5126 BEAST_EXPECT(sle !=
nullptr);
5127 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(0).value());
5128 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(100).value());
5129 auto const sharesAfter = env.balance(depositor, shares);
5130 BEAST_EXPECT(sharesAfter == sharesBefore);
5136 Account const depositor{
"bob"};
5137 Account const issuer{
"issuer"};
5139 env.fund(XRP(10000), issuer, owner, depositor);
5144 testCase(xrp,
"XRP", owner, depositor, issuer);
5148 env(fset(issuer, asfAllowTrustLineClawback));
5150 env.trust(iou(2000), owner);
5151 env.trust(iou(2000), depositor);
5152 env(pay(issuer, owner, iou(2000)));
5153 env(pay(issuer, depositor, iou(2000)));
5155 testCase(iou,
"IOU", owner, depositor, issuer);
5158 MPTTester mptt{env, issuer, kMptInitNoFund};
5159 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
5162 mptt.authorize({.account = owner});
5163 mptt.authorize({.account = depositor});
5164 env(pay(issuer, depositor, mpt(2000)));
5166 testCase(mpt,
"MPT", owner, depositor, issuer);
5172 "VaultClawback (asset) - IOU pre-fixCleanup3_1_3"
5173 " zero-amount clawback unclamped with outstanding loan");
5175 env.disableFeature(fixCleanup3_1_3);
5177 auto [vault, vaultKeylet] = setupVault(iou, owner, depositor, issuer);
5179 auto const vaultSle = env.le(vaultKeylet);
5180 BEAST_EXPECT(vaultSle !=
nullptr);
5188 env(
set(owner, vaultKeylet.key));
5193 env(
set(depositor, brokerKeylet.key, iou(40).value()),
5196 kPaymentInterval(120),
5198 Sig(sfCounterpartySignature, owner),
5199 Fee(env.current()->fees().base * 2),
5204 auto const sle = env.le(vaultKeylet);
5205 BEAST_EXPECT(sle->at(sfAssetsAvailable) == iou(60).value());
5206 BEAST_EXPECT(sle->at(sfAssetsTotal) == iou(100).value());
5209 auto const sharesBefore = env.balance(depositor, shares);
5215 env(vault.clawback({
5217 .id = vaultKeylet.key,
5218 .holder = depositor,
5225 auto const sle = env.le(vaultKeylet);
5226 BEAST_EXPECT(sle !=
nullptr);
5227 BEAST_EXPECT(sle->at(sfAssetsAvailable) == iou(60).value());
5228 BEAST_EXPECT(sle->at(sfAssetsTotal) == iou(100).value());
5229 auto const sharesAfter = env.balance(depositor, shares);
5230 BEAST_EXPECT(sharesAfter == sharesBefore);
5233 env.enableFeature(fixCleanup3_1_3);
5244 Env env{*
this, testableAmendments()};
5246 Account const issuer{
"issuer"};
5248 Vault const vault{env};
5249 env.fund(XRP(1'000'000), issuer, owner);
5253 BEAST_EXPECT(maxInt64 ==
"9223372036854775807");
5258 BEAST_EXPECT(maxInt64Plus1 ==
"9223372036854775808");
5261 BEAST_EXPECT(initialXRP ==
"100000000000000000");
5264 BEAST_EXPECT(initialXRPPlus1 ==
"100000000000000001");
5271 auto [tx,
keylet] = vault.create({.owner = owner, .asset = xrpAsset});
5272 tx[sfData] =
"4D65746144617461";
5274 tx[sfAssetsMaximum] = maxInt64;
5278 tx[sfAssetsMaximum] = initialXRPPlus1;
5282 tx[sfAssetsMaximum] = initialXRP;
5286 tx[sfAssetsMaximum] = maxInt64Plus1;
5291 auto const insertAt = maxInt64Plus1.size() - 3;
5292 auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
"." +
5293 maxInt64Plus1.substr(insertAt);
5294 BEAST_EXPECT(decimalTest ==
"9223372036854775.808");
5295 tx[sfAssetsMaximum] = decimalTest;
5296 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5300 auto const vaultSle = env.le(newKeylet);
5301 if (!BEAST_EXPECT(vaultSle))
5304 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
5311 MPTTester mptt{env, issuer, kMptInitNoFund};
5312 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
5315 mptt.authorize({.account = owner});
5320 env(pay(issuer, owner, mptAsset(100'000)));
5323 auto [tx,
keylet] = vault.create({.owner = owner, .asset = mptAsset});
5324 tx[sfData] =
"4D65746144617461";
5326 tx[sfAssetsMaximum] = maxInt64;
5330 tx[sfAssetsMaximum] = initialXRPPlus1;
5334 tx[sfAssetsMaximum] = initialXRP;
5338 tx[sfAssetsMaximum] = maxInt64Plus1;
5343 auto const insertAt = maxInt64Plus1.size() - 1;
5344 auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
"." +
5345 maxInt64Plus1.substr(insertAt);
5346 BEAST_EXPECT(decimalTest ==
"922337203685477580.8");
5347 tx[sfAssetsMaximum] = decimalTest;
5348 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5352 auto const vaultSle = env.le(newKeylet);
5353 if (!BEAST_EXPECT(vaultSle))
5356 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
5364 env.trust(iouAsset(1000), owner);
5365 env(pay(issuer, owner, iouAsset(200)));
5368 auto [tx,
keylet] = vault.create({.owner = owner, .asset = iouAsset});
5369 tx[sfData] =
"4D65746144617461";
5371 tx[sfAssetsMaximum] = maxInt64;
5375 tx[sfAssetsMaximum] = initialXRPPlus1;
5379 tx[sfAssetsMaximum] = initialXRP;
5383 tx[sfAssetsMaximum] = maxInt64Plus1;
5387 tx[sfAssetsMaximum] =
"1000000000000000e80";
5390 tx[sfAssetsMaximum] =
"1000000000000000e-96";
5395 auto const insertAt = maxInt64Plus1.size() - 1;
5396 auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
"." +
5397 maxInt64Plus1.substr(insertAt);
5398 BEAST_EXPECT(decimalTest ==
"922337203685477580.8");
5399 tx[sfAssetsMaximum] = decimalTest;
5400 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5404 auto const vaultSle = env.le(newKeylet);
5405 if (!BEAST_EXPECT(vaultSle))
5409 (vaultSle->at(sfAssetsMaximum) ==
5410 Number{9223372036854776, 2, Number::Normalized{}}));
5413 tx[sfAssetsMaximum] =
"9223372036854775807e40";
5414 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5418 auto const vaultSle = env.le(newKeylet);
5419 if (!BEAST_EXPECT(vaultSle))
5423 (vaultSle->at(sfAssetsMaximum) ==
5424 Number{9223372036854776, 43, Number::Normalized{}}));
5427 tx[sfAssetsMaximum] =
"9223372036854775807e-40";
5428 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5432 auto const vaultSle = env.le(newKeylet);
5433 if (!BEAST_EXPECT(vaultSle))
5437 (vaultSle->at(sfAssetsMaximum) ==
5438 Number{9223372036854776, -37, Number::Normalized{}}));
5441 tx[sfAssetsMaximum] =
"9223372036854775807e-100";
5442 auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
5447 auto const vaultSle = env.le(newKeylet);
5448 if (!BEAST_EXPECT(vaultSle))
5451 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) ==
kNumZero);
5456 tx[sfAssetsMaximum] =
"1000000000000000e81";
5461 env.setParseFailureExpected(
true);
5464 tx[sfAssetsMaximum] =
"18446744073709551617e5";
5466 BEAST_EXPECTS(
false,
"Expected parse_error for mantissa larger than uint64 max");
5468 catch (ParseError
const& e)
5470 using namespace std::string_literals;
5472 e.what() ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid data."s);
5474 env.setParseFailureExpected(
false);
5491 testcase(
"Vault deposit fails when MPT asset is escrowed");
5493 Env env{*
this, testableAmendments()};
5494 auto const baseFee = env.current()->fees().base;
5496 Account const depositor{
"depositor"};
5497 Account const issuer{
"issuer"};
5500 env.fund(XRP(10000), issuer, owner, depositor, bob);
5503 MPTTester mptt{env, issuer, kMptInitNoFund};
5505 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow});
5506 mptt.authorize({.account = owner});
5507 mptt.authorize({.account = depositor});
5508 mptt.authorize({.account = bob});
5510 env(pay(issuer, depositor, asset(100)));
5514 auto const escrowSeq = env.seq(depositor);
5515 env(escrow::create(depositor, bob, asset(60)),
5516 escrow::kCondition(escrow::kCb1),
5517 escrow::kFinishTime(env.now() + 1s),
5522 Vault const vault{env};
5523 auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
5529 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
5534 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(40)}),
5539 auto const sle = env.le(vaultKeylet);
5540 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(40).value());
5544 env(escrow::finish(bob, depositor, escrowSeq),
5545 escrow::kCondition(escrow::kCb1),
5546 escrow::kFulfillment(escrow::kFb1),
5553 testcase(
"Vault withdraw respects escrowed shares");
5555 Env env{*
this, testableAmendments()};
5556 auto const baseFee = env.current()->fees().base;
5558 Account const depositor{
"depositor"};
5559 Account const issuer{
"issuer"};
5562 env.fund(XRP(10000), issuer, owner, depositor, bob);
5565 MPTTester mptt{env, issuer, kMptInitNoFund};
5567 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow});
5568 mptt.authorize({.account = owner});
5569 mptt.authorize({.account = depositor});
5571 env(pay(issuer, depositor, asset(100)));
5574 Vault const vault{env};
5575 auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
5581 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
5585 auto const vaultSle = env.le(vaultKeylet);
5586 if (!BEAST_EXPECT(vaultSle))
5588 env.memoize(
Account(
"vault", vaultSle->at(sfAccount)));
5592 auto const shareMPTID = vaultSle->at(sfShareMPTID);
5595 jv[jss::Account] = bob.human();
5596 jv[sfMPTokenIssuanceID] =
to_string(shareMPTID);
5597 jv[jss::TransactionType] = jss::MPTokenAuthorize;
5603 auto const escrowAmount = shares(
Number{6, vaultSle->at(sfScale) + 1});
5604 env(escrow::create(depositor, bob, escrowAmount),
5605 escrow::kCondition(escrow::kCb1),
5606 escrow::kFinishTime(env.now() + 1s),
5613 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
5619 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(40)}),
5624 auto const sle = env.le(vaultKeylet);
5625 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(60).value());
5630 testcase(
"Vault clawback only recovers unlocked shares");
5632 Env env{*
this, testableAmendments() | fixCleanup3_1_3};
5633 auto const baseFee = env.current()->fees().base;
5635 Account const depositor{
"depositor"};
5636 Account const issuer{
"issuer"};
5639 env.fund(XRP(10000), issuer, owner, depositor, bob);
5642 MPTTester mptt{env, issuer, kMptInitNoFund};
5644 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTCanEscrow});
5645 mptt.authorize({.account = owner});
5646 mptt.authorize({.account = depositor});
5648 env(pay(issuer, depositor, asset(100)));
5651 Vault const vault{env};
5652 auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
5658 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
5662 auto const vaultSle = env.le(vaultKeylet);
5663 if (!BEAST_EXPECT(vaultSle))
5665 env.memoize(
Account(
"vault", vaultSle->at(sfAccount)));
5669 auto const shareMPTID = vaultSle->at(sfShareMPTID);
5672 jv[jss::Account] = bob.human();
5673 jv[sfMPTokenIssuanceID] =
to_string(shareMPTID);
5674 jv[jss::TransactionType] = jss::MPTokenAuthorize;
5680 auto const escrowAmount = shares(
Number{6, vaultSle->at(sfScale) + 1});
5681 env(escrow::create(depositor, bob, escrowAmount),
5682 escrow::kCondition(escrow::kCb1),
5683 escrow::kFinishTime(env.now() + 1s),
5690 env(vault.clawback({
5692 .id = vaultKeylet.key,
5693 .holder = depositor,
5699 auto const sle = env.le(vaultKeylet);
5700 BEAST_EXPECT(sle !=
nullptr);
5702 BEAST_EXPECT(sle->at(sfAssetsTotal) == asset(60).value());
5703 BEAST_EXPECT(sle->at(sfAssetsAvailable) == asset(60).value());
5706 auto const sharesAfter = env.balance(depositor, shares);
5707 BEAST_EXPECT(sharesAfter == shares(0));
7040 auto readReferenceHolding = [&](Env
const& env,
7042 auto const sleVault = env.le(vaultKeylet);
7044 return std::nullopt;
7046 if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding))
7047 return std::nullopt;
7048 return sleIssuance->getFieldH256(sfReferenceHolding);
7055 testcase(
"sfReferenceHolding: MPT-backed vault, post-amendment");
7056 Env env{*
this, testableAmendments()};
7057 Account const issuer{
"issuer"};
7059 env.fund(XRP(10'000), issuer, owner);
7062 MPTTester mptt{env, issuer, kMptInitNoFund};
7063 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
7065 mptt.authorize({.account = owner});
7067 Vault const vault{env};
7068 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7072 auto const sleVault = env.le(
keylet);
7073 BEAST_EXPECT(sleVault !=
nullptr);
7074 auto const pseudoId = sleVault->at(sfAccount);
7075 auto const expected =
keylet::mptoken(mptt.issuanceID(), pseudoId).key;
7077 auto const stored = readReferenceHolding(env,
keylet);
7078 BEAST_EXPECT(stored.has_value());
7079 BEAST_EXPECT(stored && *stored == expected);
7081 BEAST_EXPECT(env.le(
keylet::mptoken(mptt.issuanceID(), pseudoId)) !=
nullptr);
7085 testcase(
"sfReferenceHolding: IOU-backed vault, post-amendment");
7086 Env env{*
this, testableAmendments()};
7087 Account const issuer{
"issuer"};
7089 env.fund(XRP(10'000), issuer, owner);
7090 env(fset(issuer, asfDefaultRipple));
7094 env.trust(asset(1'000'000), owner);
7097 Vault const vault{env};
7098 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7102 auto const sleVault = env.le(
keylet);
7103 BEAST_EXPECT(sleVault !=
nullptr);
7104 auto const pseudoId = sleVault->at(sfAccount);
7107 auto const stored = readReferenceHolding(env,
keylet);
7108 BEAST_EXPECT(stored.has_value());
7109 BEAST_EXPECT(stored && *stored == expected);
7117 testcase(
"sfReferenceHolding: XRP-backed vault, field absent");
7118 Env env{*
this, testableAmendments()};
7120 env.fund(XRP(10'000), owner);
7124 Vault const vault{env};
7125 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7129 BEAST_EXPECT(!readReferenceHolding(env,
keylet).has_value());
7135 testcase(
"sfReferenceHolding: vault share, pre-amendment");
7136 Env env{*
this, testableAmendments() - fixCleanup3_2_0};
7137 Account const issuer{
"issuer"};
7139 env.fund(XRP(10'000), issuer, owner);
7142 MPTTester mptt{env, issuer, kMptInitNoFund};
7143 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
7145 mptt.authorize({.account = owner});
7147 Vault const vault{env};
7148 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7152 BEAST_EXPECT(!readReferenceHolding(env,
keylet).has_value());
7159 testcase(
"sfReferenceHolding: plain MPT issuance never set");
7160 Env env{*
this, testableAmendments()};
7161 Account const issuer{
"issuer"};
7162 env.fund(XRP(10'000), issuer);
7165 MPTTester mptt{env, issuer, kMptInitNoFund};
7166 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
7170 if (BEAST_EXPECT(sleIssuance))
7171 BEAST_EXPECT(!sleIssuance->isFieldPresent(sfReferenceHolding));
7191 auto referencedHoldingExists = [&](Env
const& env,
Keylet const& vaultKeylet) ->
bool {
7192 auto const sleVault = env.le(vaultKeylet);
7196 if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding))
7198 auto const holdingKey = sleIssuance->getFieldH256(sfReferenceHolding);
7204 testcase(
"vault pseudo MPToken: Clawback blocked by tecPSEUDO_ACCOUNT");
7205 Env env{*
this, testableAmendments()};
7206 Account const issuer{
"issuer"};
7208 Account const depositor{
"depositor"};
7209 env.fund(XRP(10'000), issuer, owner, depositor);
7212 MPTTester mptt{env, issuer, kMptInitNoFund};
7213 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback});
7215 mptt.authorize({.account = owner});
7216 mptt.authorize({.account = depositor});
7217 env(pay(issuer, depositor, asset(1'000)));
7220 Vault const vault{env};
7221 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7225 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)}));
7228 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7230 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
7240 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7242 BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500);
7246 testcase(
"vault pseudo MPToken: Issuer cannot Unauthorize pseudo");
7247 Env env{*
this, testableAmendments()};
7248 Account const issuer{
"issuer"};
7250 env.fund(XRP(10'000), issuer, owner);
7253 MPTTester mptt{env, issuer, kMptInitNoFund};
7254 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
7256 mptt.authorize({.account = owner});
7257 mptt.authorize({.account = issuer, .holder = owner});
7260 Vault const vault{env};
7261 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7265 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7267 auto const pseudoId = env.le(
keylet)->at(sfAccount);
7275 jv[sfAccount] = issuer.human();
7277 jv[sfMPTokenIssuanceID] =
to_string(mptt.issuanceID());
7278 jv[sfFlags] = tfMPTUnauthorize;
7279 jv[sfTransactionType] = jss::MPTokenAuthorize;
7282 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7286 testcase(
"vault pseudo MPToken: MPTokenIssuanceDestroy blocked while vault holds");
7287 Env env{*
this, testableAmendments()};
7288 Account const issuer{
"issuer"};
7290 Account const depositor{
"depositor"};
7291 env.fund(XRP(10'000), issuer, owner, depositor);
7294 MPTTester mptt{env, issuer, kMptInitNoFund};
7295 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
7297 mptt.authorize({.account = owner});
7298 mptt.authorize({.account = depositor});
7299 env(pay(issuer, depositor, asset(1'000)));
7302 Vault const vault{env};
7303 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7307 env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)}));
7310 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7319 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7324 testcase(
"vault pseudo trust line: Clawback blocked by tecPSEUDO_ACCOUNT");
7325 Env env{*
this, testableAmendments()};
7326 Account const issuer{
"issuer"};
7328 env.fund(XRP(10'000), issuer, owner);
7329 env(fset(issuer, asfAllowTrustLineClawback));
7333 env.trust(asset(1'000'000), owner);
7334 env(pay(issuer, owner, asset(1'000)));
7337 Vault const vault{env};
7338 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7342 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)}));
7345 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7347 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
7358 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7360 BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500);
7364 testcase(
"vault pseudo trust line: TrustSet limit=0 from issuer preserves line");
7365 Env env{*
this, testableAmendments()};
7366 Account const issuer{
"issuer"};
7368 env.fund(XRP(10'000), issuer, owner);
7369 env(fset(issuer, asfDefaultRipple));
7373 env.trust(asset(1'000'000), owner);
7374 env(pay(issuer, owner, asset(1'000)));
7377 Vault const vault{env};
7378 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7382 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)}));
7385 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7393 Account const pseudoAccount{
"vault-pseudo", env.le(
keylet)->at(sfAccount)};
7394 env(trust(issuer, pseudoAccount[
"IOU"](0)));
7396 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7401 testcase(
"vault pseudo holding: VaultDelete is the legitimate cleanup path");
7402 Env env{*
this, testableAmendments()};
7403 Account const issuer{
"issuer"};
7405 env.fund(XRP(10'000), issuer, owner);
7408 MPTTester mptt{env, issuer, kMptInitNoFund};
7409 mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
7411 mptt.authorize({.account = owner});
7413 Vault const vault{env};
7414 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7418 BEAST_EXPECT(referencedHoldingExists(env,
keylet));
7419 auto const pseudoId = env.le(
keylet)->at(sfAccount);
7420 auto const sharedMptId = env.le(
keylet)->at(sfShareMPTID);
7421 auto const holdingKeylet =
keylet::mptoken(mptt.issuanceID(), pseudoId);
7426 env(vault.del({.owner = owner, .id = keylet.key}));
7429 BEAST_EXPECT(env.le(
keylet) ==
nullptr);
7430 BEAST_EXPECT(env.le(holdingKeylet) ==
nullptr);
7519 Account const issuer{
"issuer"};
7524 testcase(
"VaultDeposit IOU freeze checks");
7528 env.fund(XRP(100'000), issuer, owner);
7529 env(fset(issuer, asfAllowTrustLineClawback));
7532 env.trust(asset(1'000'000), owner);
7533 env(pay(issuer, owner, asset(100'000)));
7536 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7539 auto const vaultAcct =
Account(
"vault", env.le(
keylet)->at(sfAccount));
7542 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
7545 auto runTests = [&]() {
7546 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
7549 env(fset(issuer, asfGlobalFreeze));
7550 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7552 env(fclear(issuer, asfGlobalFreeze));
7555 env(trust(issuer, asset(0), owner, tfSetFreeze));
7556 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7558 env(trust(issuer, asset(0), owner, tfClearFreeze));
7561 env(trust(issuer, asset(0), owner, tfSetFreeze | tfSetDeepFreeze));
7562 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7564 env(trust(issuer, asset(0), owner, tfClearFreeze | tfClearDeepFreeze));
7571 auto trustSet = [&]() {
7573 jv[jss::Account] = issuer.human();
7575 auto& ja = jv[jss::LimitAmount] =
7577 ja[jss::issuer] =
toBase58(vaultAcct.id());
7579 jv[jss::TransactionType] = jss::TrustSet;
7583 trustSet[jss::Flags] = tfSetFreeze;
7588 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7591 trustSet[jss::Flags] = tfClearFreeze;
7598 auto trustSet = [&]() {
7600 jv[jss::Account] = issuer.human();
7602 auto& ja = jv[jss::LimitAmount] =
7604 ja[jss::issuer] =
toBase58(vaultAcct.id());
7606 jv[jss::TransactionType] = jss::TrustSet;
7610 trustSet[jss::Flags] = tfSetFreeze | tfSetDeepFreeze;
7614 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7617 trustSet[jss::Flags] = tfClearFreeze | tfClearDeepFreeze;
7623 env(fset(issuer, asfGlobalFreeze));
7625 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(1)}));
7626 env(fclear(issuer, asfGlobalFreeze));
7627 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
7632 env.disableFeature(fixCleanup3_3_0);
7634 env.enableFeature(fixCleanup3_3_0);
7639 testcase(
"VaultDeposit MPT lock checks");
7643 env.fund(XRP(100'000), issuer, owner);
7646 MPTTester mptt{env, issuer, kMptInitNoFund};
7648 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
7651 mptt.authorize({.account = owner});
7652 mptt.authorize({.account = issuer, .holder = owner});
7654 env(pay(issuer, owner, mpt(100'000)));
7657 auto [tx,
keylet] = vault.create({.owner = owner, .asset = mpt});
7660 auto const vaultAcctID = env.le(
keylet)->at(sfAccount);
7661 Account const vaultAcct(
"vault", vaultAcctID);
7663 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(100)}));
7668 auto runTests = [&]() {
7670 mptt.set({.flags = tfMPTLock});
7672 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
7674 mptt.set({.flags = tfMPTUnlock});
7678 mptt.set({.holder = owner, .flags = tfMPTLock});
7680 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
7682 mptt.set({.holder = owner, .flags = tfMPTUnlock});
7686 mptt.set({.holder = vaultAcct, .flags = tfMPTLock});
7688 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
7690 mptt.set({.holder = vaultAcct, .flags = tfMPTUnlock});
7694 mptt.set({.flags = tfMPTLock});
7697 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = mpt(1)}));
7698 mptt.set({.flags = tfMPTUnlock});
7700 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
7705 env.disableFeature(fixCleanup3_3_0);
7707 env.enableFeature(fixCleanup3_3_0);
7789 Account const issuer{
"issuer"};
7794 testcase(
"VaultWithdraw IOU freeze checks");
7798 env.fund(XRP(100'000), issuer, owner);
7799 env(fset(issuer, asfAllowTrustLineClawback));
7802 env.trust(asset(1'000'000), owner);
7803 env(pay(issuer, owner, asset(100'000)));
7806 auto [tx,
keylet] = vault.create({.owner = owner, .asset = asset});
7809 auto const vaultAcct =
Account(
"vault", env.le(
keylet)->at(sfAccount));
7811 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
7814 Account const charlie{
"charlie"};
7815 env.fund(XRP(10'000), charlie);
7816 env.trust(asset(1'000'000), charlie);
7819 auto runTests = [&]() {
7820 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
7827 env(fset(issuer, asfGlobalFreeze));
7828 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7832 auto withdrawToCharlie =
7833 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
7834 withdrawToCharlie[sfDestination] = charlie.human();
7837 env(fclear(issuer, asfGlobalFreeze));
7841 auto trustSet = [&]() {
7843 jv[jss::Account] = issuer.human();
7845 auto& ja = jv[jss::LimitAmount] =
7847 ja[jss::issuer] =
toBase58(vaultAcct.id());
7849 jv[jss::TransactionType] = jss::TrustSet;
7853 trustSet[jss::Flags] = tfSetFreeze;
7860 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7861 Ter(vaultAcctFreeze));
7864 auto withdrawToCharlie = vault.withdraw(
7865 {.depositor = owner, .id =
keylet.key, .amount = asset(1)});
7866 withdrawToCharlie[sfDestination] = charlie.human();
7867 env(withdrawToCharlie, Ter(vaultAcctFreeze));
7870 trustSet[jss::Flags] = tfClearFreeze;
7876 env(trust(issuer, asset(0), owner, tfSetFreeze));
7879 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7884 auto withdrawTo3rd =
7885 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
7886 withdrawTo3rd[sfDestination] = charlie.human();
7887 env(withdrawTo3rd, Ter(submitterTo3rd));
7889 env(trust(issuer, asset(0), owner, tfClearFreeze));
7893 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
7898 env(trust(issuer, asset(0), owner, tfSetFreeze | tfSetDeepFreeze));
7901 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}),
7904 env(trust(issuer, asset(0), owner, tfClearFreeze | tfClearDeepFreeze));
7907 env(trust(issuer, asset(0), charlie, tfSetFreeze));
7909 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
7911 auto withdrawToCharlie =
7912 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
7913 withdrawToCharlie[sfDestination] = charlie.human();
7918 env(trust(issuer, asset(0), charlie, tfClearFreeze));
7921 {.depositor = owner,
7923 .amount = asset(fix330Enabled ? 2 : 1)}));
7927 env(trust(issuer, asset(0), charlie, tfSetFreeze | tfSetDeepFreeze));
7929 auto withdrawToCharlie =
7930 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = asset(1)});
7931 withdrawToCharlie[sfDestination] = charlie.human();
7935 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
7936 env(trust(issuer, asset(0), charlie, tfClearFreeze | tfClearDeepFreeze));
7937 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
7941 env(fset(issuer, asfGlobalFreeze));
7943 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(1)}));
7944 env(fclear(issuer, asfGlobalFreeze));
7945 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(1)}));
7950 env.disableFeature(fixCleanup3_3_0);
7952 env.enableFeature(fixCleanup3_3_0);
7957 testcase(
"VaultWithdraw MPT lock checks");
7961 env.fund(XRP(100'000), issuer, owner);
7964 MPTTester mptt{env, issuer, kMptInitNoFund};
7966 {.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
7969 mptt.authorize({.account = owner});
7970 mptt.authorize({.account = issuer, .holder = owner});
7972 env(pay(issuer, owner, mpt(100'000)));
7975 auto [tx,
keylet] = vault.create({.owner = owner, .asset = mpt});
7978 Account const vaultAcct(
"vault", env.le(
keylet)->at(sfAccount));
7980 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(100)}));
7983 Account const charlie{
"charlie"};
7984 env.fund(XRP(10'000), charlie);
7986 mptt.authorize({.account = charlie});
7987 mptt.authorize({.account = issuer, .holder = charlie});
7990 auto runTests = [&]() {
7991 auto const fix330Enabled = env.current()->rules().enabled(fixCleanup3_3_0);
7994 mptt.set({.flags = tfMPTLock});
7996 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
8004 auto withdrawToIssuer =
8005 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
8006 withdrawToIssuer[sfDestination] = issuer.human();
8009 mptt.set({.flags = tfMPTUnlock});
8013 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
8018 mptt.set({.holder = vaultAcct, .flags = tfMPTLock});
8020 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
8022 mptt.set({.holder = vaultAcct, .flags = tfMPTUnlock});
8027 mptt.set({.holder = owner, .flags = tfMPTLock});
8029 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}),
8033 auto withdrawToCharlie =
8034 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
8035 withdrawToCharlie[sfDestination] = charlie.human();
8043 auto withdrawToIssuer =
8044 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
8045 withdrawToIssuer[sfDestination] = issuer.human();
8048 mptt.set({.holder = owner, .flags = tfMPTUnlock});
8052 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
8057 mptt.set({.holder = charlie, .flags = tfMPTLock});
8060 auto withdrawToCharlie =
8061 vault.withdraw({.depositor = owner, .id =
keylet.key, .amount = mpt(1)});
8062 withdrawToCharlie[sfDestination] = charlie.human();
8066 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
8067 mptt.set({.holder = charlie, .flags = tfMPTUnlock});
8069 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
8073 mptt.set({.flags = tfMPTLock});
8076 {.issuer = issuer, .id = keylet.key, .holder = owner, .amount = mpt(1)}));
8077 mptt.set({.flags = tfMPTUnlock});
8079 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = mpt(1)}));
8084 env.disableFeature(fixCleanup3_3_0);
8086 env.enableFeature(fixCleanup3_3_0);