47 using namespace test::jtx;
48 Account issuer{
"issuer"};
49 Account owner{
"owner"};
50 Account depositor{
"depositor"};
51 Account charlie{
"charlie"};
54 auto const testSequence = [&,
this](
59 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
60 tx[sfData] =
"AFEED00E";
61 tx[sfAssetsMaximum] = asset(100).number();
64 BEAST_EXPECT(env.le(keylet));
67 auto const [share, vaultAccount] =
72 auto const vault = env.le(keylet);
73 BEAST_EXPECT(vault !=
nullptr);
74 if (!asset.integral())
75 BEAST_EXPECT(vault->at(sfScale) == 6);
77 BEAST_EXPECT(vault->at(sfScale) == 0);
80 BEAST_EXPECT(shares !=
nullptr);
81 if (!asset.integral())
82 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
84 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
87 Account(
"vault", vault->at(sfAccount))};
89 auto const shares = share.raw().get<
MPTIssue>();
90 env.memoize(vaultAccount);
93 Account alice{
"alice"};
95 env.fund(XRP(1000), alice, erin);
100 testcase(prefix +
" fail to deposit more than assets held");
101 auto tx = vault.deposit(
102 {.depositor = depositor,
104 .amount = asset(10000)});
110 testcase(prefix +
" deposit non-zero amount");
111 auto tx = vault.deposit(
112 {.depositor = depositor,
114 .amount = asset(50)});
118 env.balance(depositor, shares) == share(50 * scale));
122 testcase(prefix +
" deposit non-zero amount again");
123 auto tx = vault.deposit(
124 {.depositor = depositor,
126 .amount = asset(50)});
130 env.balance(depositor, shares) == share(100 * scale));
134 testcase(prefix +
" fail to delete non-empty vault");
135 auto tx = vault.del({.owner = owner, .id = keylet.
key});
141 testcase(prefix +
" fail to update because wrong owner");
142 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
143 tx[sfAssetsMaximum] = asset(50).number();
150 prefix +
" fail to set maximum lower than current amount");
151 auto tx = vault.set({.owner = owner, .id = keylet.
key});
152 tx[sfAssetsMaximum] = asset(50).number();
158 testcase(prefix +
" set maximum higher than current amount");
159 auto tx = vault.set({.owner = owner, .id = keylet.
key});
160 tx[sfAssetsMaximum] = asset(150).number();
166 testcase(prefix +
" set maximum is idempotent, set it again");
167 auto tx = vault.set({.owner = owner, .id = keylet.
key});
168 tx[sfAssetsMaximum] = asset(150).number();
175 auto tx = vault.set({.owner = owner, .id = keylet.
key});
182 testcase(prefix +
" fail to set domain on public vault");
183 auto tx = vault.set({.owner = owner, .id = keylet.
key});
190 testcase(prefix +
" fail to deposit more than maximum");
191 auto tx = vault.deposit(
192 {.depositor = depositor,
194 .amount = asset(100)});
200 testcase(prefix +
" reset maximum to zero i.e. not enforced");
201 auto tx = vault.set({.owner = owner, .id = keylet.
key});
202 tx[sfAssetsMaximum] = asset(0).number();
208 testcase(prefix +
" fail to withdraw more than assets held");
209 auto tx = vault.withdraw(
210 {.depositor = depositor,
212 .amount = asset(1000)});
218 testcase(prefix +
" deposit some more");
219 auto tx = vault.deposit(
220 {.depositor = depositor,
222 .amount = asset(100)});
226 env.balance(depositor, shares) == share(200 * scale));
230 testcase(prefix +
" clawback some");
233 auto tx = vault.clawback(
237 .amount = asset(10)});
240 if (!asset.raw().native())
243 env.balance(depositor, shares) == share(190 * scale));
251 auto tx = vault.clawback(
252 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
255 if (!asset.raw().native())
257 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
260 auto tx = vault.clawback(
264 .amount = asset(10)});
270 auto tx = vault.withdraw(
271 {.depositor = depositor,
273 .amount = asset(10)});
280 if (!asset.raw().native())
282 testcase(prefix +
" deposit again");
283 auto tx = vault.deposit(
284 {.depositor = depositor,
286 .amount = asset(200)});
290 env.balance(depositor, shares) == share(200 * scale));
294 testcase(prefix +
" deposit/withdrawal same or less than fee");
295 auto const amount = env.current()->fees().base;
297 auto tx = vault.deposit(
298 {.depositor = depositor,
305 {.depositor = depositor,
312 {.depositor = depositor,
320 {.depositor = depositor,
323 tx[sfDestination] = charlie.human();
328 {.depositor = depositor,
330 .amount = amount - 1});
335 {.depositor = depositor,
337 .amount = amount - 1});
344 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
345 auto tx = vault.withdraw(
346 {.depositor = depositor,
348 .amount = asset(100)});
349 tx[sfDestination] = alice.human();
355 testcase(prefix +
" fail to withdraw to zero destination");
356 auto tx = vault.withdraw(
357 {.depositor = depositor,
359 .amount = asset(1000)});
360 tx[sfDestination] =
"0";
365 if (!asset.raw().native())
368 prefix +
" fail to withdraw to 3rd party no authorization");
369 auto tx = vault.withdraw(
370 {.depositor = depositor,
372 .amount = asset(100)});
373 tx[sfDestination] = erin.human();
382 " fail to withdraw to 3rd party lsfRequireDestTag");
383 auto tx = vault.withdraw(
384 {.depositor = depositor,
386 .amount = asset(100)});
387 tx[sfDestination] = dave.human();
393 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
394 auto tx = vault.withdraw(
395 {.depositor = depositor,
397 .amount = asset(50)});
398 tx[sfDestination] = dave.human();
399 tx[sfDestinationTag] =
"0";
405 testcase(prefix +
" deposit again");
406 auto tx = vault.deposit(
407 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
413 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
414 auto tx = vault.withdraw(
415 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
421 testcase(prefix +
" withdraw with tag");
422 auto tx = vault.withdraw(
423 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
424 tx[sfDestinationTag] =
"0";
430 testcase(prefix +
" withdraw to authorized 3rd party");
431 auto tx = vault.withdraw(
432 {.depositor = depositor,
434 .amount = asset(50)});
435 tx[sfDestination] = charlie.human();
439 env.balance(depositor, shares) == share(100 * scale));
443 testcase(prefix +
" withdraw to issuer");
444 auto tx = vault.withdraw(
445 {.depositor = depositor,
447 .amount = asset(50)});
448 tx[sfDestination] = issuer.human();
452 env.balance(depositor, shares) == share(50 * scale));
455 if (!asset.raw().native())
457 testcase(prefix +
" issuer deposits");
458 auto tx = vault.deposit(
459 {.depositor = issuer,
461 .amount = asset(10)});
464 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
466 testcase(prefix +
" issuer withdraws");
468 {.depositor = issuer,
470 .amount = share(10 * scale)});
473 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
477 testcase(prefix +
" withdraw remaining assets");
478 auto tx = vault.withdraw(
479 {.depositor = depositor,
481 .amount = asset(50)});
484 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
486 if (!asset.raw().native())
488 auto tx = vault.clawback(
492 .amount = asset(0)});
498 auto tx = vault.withdraw(
499 {.depositor = depositor,
501 .amount = share(10)});
507 if (!asset.integral())
509 testcase(prefix +
" temporary authorization for 3rd party");
510 env(trust(erin, asset(1000)));
511 env(trust(issuer, asset(0), erin,
tfSetfAuth));
512 env(pay(issuer, erin, asset(10)));
515 auto tx = vault.deposit(
516 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
520 auto tx = pay(erin, depositor, share(10 * scale));
528 {.depositor = depositor,
530 .amount = asset(1)}));
537 testcase(prefix +
" withdraw to authorized 3rd party");
540 {.depositor = depositor,
542 .amount = asset(10)});
543 tx[sfDestination] = erin.human();
548 env(pay(erin, issuer, asset(10)));
551 testcase(prefix +
" fail to pay to unauthorized 3rd party");
552 env(trust(erin, asset(0)));
556 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
561 {.depositor = depositor,
563 .amount = asset(1)});
569 testcase(prefix +
" fail to delete because wrong owner");
570 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
576 testcase(prefix +
" delete empty vault");
577 auto tx = vault.del({.owner = owner, .id = keylet.
key});
580 BEAST_EXPECT(!env.le(keylet));
584 auto testCases = [&,
this](
587 Env env{*
this, testable_amendments() | featureSingleAssetVault};
590 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
600 testSequence(prefix, env, vault, asset);
607 testCases(
"IOU", [&](Env& env) ->
Asset {
609 env(trust(owner, asset(1000)));
610 env(trust(depositor, asset(1000)));
611 env(trust(charlie, asset(1000)));
612 env(trust(dave, asset(1000)));
613 env(trust(issuer, asset(0), owner,
tfSetfAuth));
614 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
615 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
616 env(trust(issuer, asset(0), dave,
tfSetfAuth));
617 env(pay(issuer, depositor, asset(1000)));
622 testCases(
"MPT", [&](Env& env) ->
Asset {
623 MPTTester mptt{env, issuer, mptInitNoFund};
627 mptt.authorize({.account = depositor});
628 mptt.authorize({.account = charlie});
629 mptt.authorize({.account = dave});
630 env(pay(issuer, depositor, asset(1000)));
639 using namespace test::jtx;
644 testable_amendments() | featureSingleAssetVault;
647 auto testCase = [&,
this](
650 Account
const& issuer,
651 Account
const& owner,
654 CaseArgs args = {}) {
655 Env env{*
this, args.features};
656 Account issuer{
"issuer"};
657 Account owner{
"owner"};
659 env.fund(XRP(1000), issuer, owner);
667 env(trust(owner, asset(1000)));
668 env(trust(issuer, asset(0), owner,
tfSetfAuth));
669 env(pay(issuer, owner, asset(1000)));
672 test(env, issuer, owner, asset, vault);
676 return [&, resultAfterCreate](
678 Account
const& issuer,
679 Account
const& owner,
682 testcase(
"disabled single asset vault");
685 vault.create({.owner = owner, .asset = asset});
689 auto tx = vault.set({.owner = owner, .id = keylet.
key});
690 env(tx, data(
"test"), ter{resultAfterCreate});
694 auto tx = vault.deposit(
697 .amount = asset(10)});
698 env(tx, ter{resultAfterCreate});
702 auto tx = vault.withdraw(
705 .amount = asset(10)});
706 env(tx, ter{resultAfterCreate});
710 auto tx = vault.clawback(
714 .amount = asset(10)});
715 env(tx, ter{resultAfterCreate});
719 auto tx = vault.del({.owner = owner, .id = keylet.
key});
720 env(tx, ter{resultAfterCreate});
727 {.features = testable_amendments() - featureSingleAssetVault});
731 {.features = testable_amendments() - featureMPTokensV1});
735 Account
const& issuer,
736 Account
const& owner,
739 testcase(
"disabled permissioned domains");
742 vault.create({.owner = owner, .asset = asset});
750 auto tx = vault.set({.owner = owner, .id = keylet.
key});
751 env(tx, data(
"Test"));
757 {.features = testable_amendments() - featurePermissionedDomains});
759 testCase([&](Env& env,
760 Account
const& issuer,
761 Account
const& owner,
766 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
771 auto tx = vault.set({.owner = owner, .id = keylet.
key});
777 auto tx = vault.deposit(
780 .amount = asset(10)});
786 auto tx = vault.withdraw(
789 .amount = asset(10)});
795 auto tx = vault.clawback(
799 .amount = asset(10)});
805 auto tx = vault.del({.owner = owner, .id = keylet.
key});
811 testCase([&](Env& env,
812 Account
const& issuer,
813 Account
const& owner,
818 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
823 auto tx = vault.set({.owner = owner, .id = keylet.
key});
829 auto tx = vault.deposit(
832 .amount = asset(10)});
838 auto tx = vault.withdraw(
841 .amount = asset(10)});
847 auto tx = vault.clawback(
851 .amount = asset(10)});
857 auto tx = vault.del({.owner = owner, .id = keylet.
key});
866 Account
const& owner,
869 testcase(
"disabled permissioned domain");
872 vault.create({.owner = owner, .asset =
xrpIssue()});
877 auto tx = vault.set({.owner = owner, .id = keylet.
key});
883 auto tx = vault.set({.owner = owner, .id = keylet.
key});
884 tx[sfDomainID] =
"0";
888 {.features = (testable_amendments() | featureSingleAssetVault) -
889 featurePermissionedDomains});
891 testCase([&](Env& env,
892 Account
const& issuer,
893 Account
const& owner,
899 vault.create({.owner = owner, .asset =
xrpIssue()});
902 auto tx = vault.set({
910 auto tx = vault.deposit(
913 .amount = asset(10)});
918 auto tx = vault.withdraw(
921 .amount = asset(10)});
926 auto tx = vault.clawback(
930 .amount = asset(10)});
935 auto tx = vault.del({
943 testCase([&](Env& env,
944 Account
const& issuer,
945 Account
const& owner,
950 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
953 auto tx = vault.clawback(
957 .amount = asset(10)});
962 testCase([&](Env& env,
964 Account
const& owner,
967 testcase(
"withdraw to bad destination");
969 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
972 auto tx = vault.withdraw(
975 .amount = asset(10)});
976 tx[jss::Destination] =
"0";
981 testCase([&](Env& env,
983 Account
const& owner,
990 vault.create({.owner = owner, .asset = asset});
997 vault.create({.owner = owner, .asset = asset});
1005 vault.create({.owner = owner, .asset = asset});
1009 auto const sleVault = env.le(keylet);
1010 BEAST_EXPECT(sleVault);
1011 BEAST_EXPECT((*sleVault)[sfScale] == 18);
1016 vault.create({.owner = owner, .asset = asset});
1020 auto const sleVault = env.le(keylet);
1021 BEAST_EXPECT(sleVault);
1022 BEAST_EXPECT((*sleVault)[sfScale] == 0);
1027 vault.create({.owner = owner, .asset = asset});
1030 auto const sleVault = env.le(keylet);
1031 BEAST_EXPECT(sleVault);
1032 BEAST_EXPECT((*sleVault)[sfScale] == 6);
1036 testCase([&](Env& env,
1038 Account
const& owner,
1041 testcase(
"create or set invalid data");
1043 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1059 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1065 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1072 testCase([&](Env& env,
1074 Account
const& owner,
1079 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1082 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1087 testCase([&](Env& env,
1089 Account
const& owner,
1092 testcase(
"create with invalid metadata");
1094 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1098 tx[sfMPTokenMetadata] =
"";
1111 testCase([&](Env& env,
1113 Account
const& owner,
1118 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1121 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1127 testCase([&](Env& env,
1129 Account
const& owner,
1132 testcase(
"invalid deposit amount");
1134 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1137 auto tx = vault.deposit(
1138 {.depositor = owner,
1145 auto tx = vault.deposit(
1146 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1151 testCase([&](Env& env,
1153 Account
const& owner,
1156 testcase(
"invalid set immutable flag");
1158 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1161 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1167 testCase([&](Env& env,
1169 Account
const& owner,
1172 testcase(
"invalid withdraw amount");
1174 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1177 auto tx = vault.withdraw(
1178 {.depositor = owner,
1185 auto tx = vault.withdraw(
1186 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1191 testCase([&](Env& env,
1192 Account
const& issuer,
1193 Account
const& owner,
1198 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1201 auto tx = vault.clawback(
1205 .amount = asset(50)});
1210 auto tx = vault.clawback(
1219 testCase([&](Env& env,
1221 Account
const& owner,
1226 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1230 tx[sfWithdrawalPolicy] = 0;
1249 tx[sfDomainID] =
"0";
1738 using namespace test::jtx;
1742 bool enableClawback =
true;
1744 int initialXRP = 1000;
1747 auto testCase = [
this](
1750 Account
const& issuer,
1751 Account
const& owner,
1752 Account
const& depositor,
1755 MPTTester& mptt)> test,
1756 CaseArgs args = {}) {
1757 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1758 Account issuer{
"issuer"};
1759 Account owner{
"owner"};
1760 Account depositor{
"depositor"};
1761 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1765 MPTTester mptt{env, issuer, mptInitNoFund};
1773 mptt.authorize({.account = owner});
1774 mptt.authorize({.account = depositor});
1775 if (args.requireAuth)
1777 mptt.authorize({.account = issuer, .holder = owner});
1778 mptt.authorize({.account = issuer, .holder = depositor});
1781 env(pay(issuer, depositor, asset(1000)));
1784 test(env, issuer, owner, depositor, asset, vault, mptt);
1789 Account
const& issuer,
1790 Account
const& owner,
1791 Account
const& depositor,
1795 testcase(
"MPT nothing to clawback from");
1796 auto tx = vault.clawback(
1799 .holder = depositor,
1800 .amount = asset(10)});
1806 Account
const& issuer,
1807 Account
const& owner,
1808 Account
const& depositor,
1812 testcase(
"MPT global lock blocks create");
1813 mptt.set({.account = issuer, .flags =
tfMPTLock});
1814 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1820 Account
const& issuer,
1821 Account
const& owner,
1822 Account
const& depositor,
1826 testcase(
"MPT global lock blocks deposit");
1827 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1831 mptt.set({.account = issuer, .flags =
tfMPTLock});
1835 {.depositor = depositor,
1837 .amount = asset(100)});
1842 tx = vault.del({.owner = owner, .id = keylet.
key});
1848 Account
const& issuer,
1849 Account
const& owner,
1850 Account
const& depositor,
1854 testcase(
"MPT global lock blocks withdrawal");
1855 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1859 {.depositor = depositor,
1861 .amount = asset(100)});
1867 auto v = env.le(keylet);
1869 MPTID share = (*v)[sfShareMPTID];
1871 BEAST_EXPECT(issuance);
1872 Number outstandingShares = issuance->at(sfOutstandingAmount);
1873 BEAST_EXPECT(outstandingShares == 100);
1875 mptt.set({.account = issuer, .flags =
tfMPTLock});
1878 tx = vault.withdraw(
1879 {.depositor = depositor,
1881 .amount = asset(100)});
1884 tx[sfDestination] = issuer.human();
1888 tx = vault.clawback(
1891 .holder = depositor,
1892 .amount = asset(0)});
1898 BEAST_EXPECT(mptSle ==
nullptr);
1901 tx = vault.del({.owner = owner, .id = keylet.
key});
1907 Account
const& issuer,
1908 Account
const& owner,
1909 Account
const& depositor,
1913 testcase(
"MPT only issuer can clawback");
1915 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1920 {.depositor = depositor,
1922 .amount = asset(100)});
1927 auto tx = vault.clawback(
1928 {.issuer = owner, .id = keylet.
key, .holder = depositor});
1936 Account
const& issuer,
1937 Account
const& owner,
1938 Account
const& depositor,
1942 testcase(
"MPT depositor without MPToken, auth required");
1945 vault.create({.owner = owner, .asset = asset});
1950 {.depositor = depositor,
1952 .amount = asset(1000)});
1962 auto const mptoken =
1964 auto const sleMPT1 = env.le(mptoken);
1965 BEAST_EXPECT(sleMPT1 ==
nullptr);
1967 tx = vault.withdraw(
1968 {.depositor = depositor,
1970 .amount = asset(100)});
1974 auto const sleMPT2 = env.le(mptoken);
1975 BEAST_EXPECT(sleMPT2 ==
nullptr);
1980 Account charlie{
"charlie"};
1981 env.fund(XRP(1000), charlie);
1984 tx = vault.withdraw(
1985 {.depositor = depositor,
1987 .amount = asset(100)});
1988 tx[sfDestination] = charlie.human();
1992 {.requireAuth =
true});
1997 Account
const& issuer,
1998 Account
const& owner,
1999 Account
const& depositor,
2003 testcase(
"MPT depositor without MPToken, no auth required");
2006 vault.create({.owner = owner, .asset = asset});
2009 auto v = env.le(keylet);
2013 {.depositor = depositor,
2015 .amount = asset(1000)});
2025 auto const mptoken =
2027 auto const sleMPT1 = env.le(mptoken);
2028 BEAST_EXPECT(sleMPT1 ==
nullptr);
2030 tx = vault.withdraw(
2031 {.depositor = depositor,
2033 .amount = asset(100)});
2037 auto const sleMPT2 = env.le(mptoken);
2038 BEAST_EXPECT(sleMPT2 !=
nullptr);
2039 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
2048 auto const mptoken =
2050 auto const sleMPT1 = env.le(mptoken);
2051 BEAST_EXPECT(sleMPT1 ==
nullptr);
2053 tx = vault.withdraw(
2054 {.depositor = depositor,
2056 .amount = asset(100)});
2057 tx[sfDestination] = owner.human();
2061 auto const sleMPT2 = env.le(mptoken);
2062 BEAST_EXPECT(sleMPT2 ==
nullptr);
2065 {.requireAuth =
false});
2068 Env env{*
this, testable_amendments()};
2070 env.current()->fees().accountReserve(0).drops() /
2072 env.current()->fees().increment.drops() /
2079 Account
const& issuer,
2080 Account
const& owner,
2081 Account
const& depositor,
2085 testcase(
"MPT failed reserve to re-create MPToken");
2088 vault.create({.owner = owner, .asset = asset});
2091 auto v = env.le(keylet);
2094 env(pay(depositor, owner, asset(1000)));
2098 {.depositor = owner,
2100 .amount = asset(1000)});
2110 auto const mptoken =
2112 auto const sleMPT = env.le(mptoken);
2113 BEAST_EXPECT(sleMPT ==
nullptr);
2116 env(ticket::create(owner, 1));
2120 tx = vault.withdraw(
2121 {.depositor = owner,
2123 .amount = asset(100)});
2127 env(pay(depositor, owner, XRP(incReserve)));
2135 {.requireAuth =
false,
2136 .initialXRP = acctReserve + incReserve * 4 + 1});
2140 Account
const& issuer,
2141 Account
const& owner,
2142 Account
const& depositor,
2148 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2153 {.depositor = depositor,
2155 .amount = asset(1000)});
2160 auto tx = vault.clawback(
2163 .holder = depositor,
2164 .amount = asset(0)});
2168 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
2173 vault.create({.owner = depositor, .asset = asset});
2178 auto tx = vault.deposit(
2179 {.depositor = depositor,
2181 .amount = asset(10)});
2186 auto tx = vault.withdraw(
2187 {.depositor = depositor,
2189 .amount = asset(10)});
2194 auto tx = vault.clawback(
2197 .holder = depositor,
2198 .amount = asset(0)});
2202 env(vault.del({.owner = owner, .id = keylet.key}));
2207 Account
const& issuer,
2208 Account
const& owner,
2209 Account
const& depositor,
2213 testcase(
"MPT vault owner can receive shares unless unauthorized");
2215 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2220 {.depositor = depositor,
2222 .amount = asset(1000)});
2227 auto const vault = env.le(keylet);
2228 return vault->at(sfShareMPTID);
2234 env(pay(depositor, owner, shares(1)));
2237 tx = vault.withdraw(
2238 {.depositor = owner,
2240 .amount = shares(1)});
2245 env(pay(depositor, owner, shares(1)));
2248 tx = vault.clawback(
2252 .amount = asset(0)});
2257 env(pay(depositor, owner, shares(1)));
2261 env(pay(owner, depositor, shares(1)));
2267 jv[sfAccount] = owner.human();
2268 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2270 jv[sfTransactionType] = jss::MPTokenAuthorize;
2276 tx = pay(depositor, owner, shares(1));
2281 tx = vault.clawback(
2284 .holder = depositor,
2285 .amount = asset(0)});
2290 env(vault.del({.owner = owner, .id = keylet.key}));
2298 Account
const& issuer,
2299 Account
const& owner,
2300 Account
const& depositor,
2307 vault.create({.owner = owner, .asset = asset});
2312 {.depositor = depositor,
2314 .amount = asset(1000)});
2319 auto tx = vault.clawback(
2322 .holder = depositor,
2323 .amount = asset(0)});
2327 {.enableClawback =
false});
2331 Account
const& issuer,
2332 Account
const& owner,
2333 Account
const& depositor,
2338 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2342 {.depositor = depositor,
2344 .amount = asset(1000)});
2350 .holder = depositor,
2355 auto tx = vault.withdraw(
2356 {.depositor = depositor,
2358 .amount = asset(100)});
2362 tx[sfDestination] = issuer.human();
2366 tx[sfDestination] = owner.human();
2373 auto tx = vault.deposit(
2374 {.depositor = depositor,
2376 .amount = asset(100)});
2381 tx = vault.clawback(
2384 .holder = depositor,
2385 .amount = asset(800)});
2389 env(vault.del({.owner = owner, .id = keylet.key}));
2394 Account
const& issuer,
2395 Account
const& owner,
2396 Account
const& depositor,
2400 testcase(
"MPT lock of vault pseudo-account");
2401 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2405 auto const vaultAccount =
2406 [&env, keylet = keylet,
this]() ->
AccountID {
2407 auto const vault = env.le(keylet);
2408 BEAST_EXPECT(vault !=
nullptr);
2409 return vault->at(sfAccount);
2413 {.depositor = depositor,
2415 .amount = asset(100)});
2421 jv[jss::Account] = issuer.human();
2422 jv[sfMPTokenIssuanceID] =
2424 jv[jss::Holder] =
toBase58(vaultAccount);
2425 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2433 {.depositor = depositor,
2435 .amount = asset(100)});
2438 tx = vault.withdraw(
2439 {.depositor = depositor,
2441 .amount = asset(100)});
2445 tx = vault.clawback(
2448 .holder = depositor,
2449 .amount = asset(100)});
2453 tx = vault.del({.owner = owner, .id = keylet.
key});
2460 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2461 Account owner{
"owner"};
2462 Account issuer{
"issuer"};
2463 env.fund(XRP(1000000), owner, issuer);
2467 MPTTester mptt{env, issuer, mptInitNoFund};
2471 mptt.authorize({.account = owner});
2472 mptt.authorize({.account = issuer, .holder = owner});
2474 env(pay(issuer, owner, asset(100)));
2475 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2479 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2480 auto const vault = env.le(keylet);
2481 BEAST_EXPECT(vault !=
nullptr);
2482 return MPTIssue(vault->at(sfShareMPTID));
2485 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2493 Account
const& owner,
2494 Account
const& depositor,
2500 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2505 {.depositor = depositor,
2507 .amount = asset(100)});
2518 tx = vault.withdraw(
2519 {.depositor = depositor,
2521 .amount = asset(100)});
2534 env(vault.del({.owner = owner, .id = keylet.key}));
2541 using namespace test::jtx;
2545 int initialXRP = 1000;
2548 bool charlieRipple =
true;
2555 Account
const& owner,
2556 Account
const& issuer,
2557 Account
const& charlie,
2562 CaseArgs args = {}) {
2563 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2564 Account
const owner{
"owner"};
2565 Account
const issuer{
"issuer"};
2566 Account
const charlie{
"charlie"};
2568 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2573 env.trust(asset(1000), owner);
2574 env(pay(issuer, owner, asset(args.initialIOU)));
2576 if (!args.charlieRipple)
2580 env.trust(asset(1000), charlie);
2582 env(pay(issuer, charlie, asset(args.initialIOU)));
2587 env.trust(asset(1000), charlie);
2589 env(rate(issuer, args.transferRate));
2592 auto const vaultAccount =
2594 return Account(
"vault", env.le(keylet)->at(sfAccount));
2597 return env.le(keylet)->at(sfShareMPTID);
2613 Account
const& owner,
2614 Account
const& issuer,
2620 testcase(
"IOU cannot use different asset");
2623 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2629 auto tx = [&, account = vaultAccount(keylet)]() {
2631 jv[jss::Account] = issuer.human();
2633 auto& ja = jv[jss::LimitAmount] =
2635 ja[jss::issuer] =
toBase58(account);
2637 jv[jss::TransactionType] = jss::TrustSet;
2646 auto tx = vault.deposit(
2647 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2653 auto tx = vault.withdraw(
2654 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2659 env(vault.del({.owner = owner, .id = keylet.key}));
2665 Account
const& owner,
2666 Account
const& issuer,
2667 Account
const& charlie,
2672 testcase(
"IOU frozen trust line to vault account");
2674 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2679 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2682 Asset const share =
Asset(issuanceId(keylet));
2685 auto trustSet = [&, account = vaultAccount(keylet)]() {
2687 jv[jss::Account] = issuer.human();
2689 auto& ja = jv[jss::LimitAmount] =
2691 ja[jss::issuer] =
toBase58(account);
2693 jv[jss::TransactionType] = jss::TrustSet;
2705 auto tx = vault.deposit(
2706 {.depositor = owner,
2708 .amount = asset(80)});
2713 auto tx = vault.withdraw(
2714 {.depositor = owner,
2716 .amount = asset(100)});
2720 tx[sfDestination] = charlie.human();
2727 auto tx = vault.clawback(
2731 .amount = asset(50)});
2742 {.depositor = owner,
2744 .amount = share(50'000'000)}));
2746 env(vault.del({.owner = owner, .id = keylet.key}));
2753 Account
const& owner,
2754 Account
const& issuer,
2755 Account
const& charlie,
2760 testcase(
"IOU transfer fees not applied");
2763 vault.create({.owner = owner, .asset = asset});
2768 {.depositor = owner,
2770 .amount = asset(100)}));
2774 Asset const share =
Asset(issuanceId(keylet));
2777 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2779 env.balance(vaultAccount(keylet), issue) == asset(100));
2782 auto tx = vault.clawback(
2786 .amount = asset(50)});
2792 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2794 env.balance(vaultAccount(keylet), issue) == asset(50));
2797 {.depositor = owner,
2799 .amount = share(20'000'000)}));
2802 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2804 env.balance(vaultAccount(keylet), issue) == asset(30));
2807 auto tx = vault.withdraw(
2808 {.depositor = owner,
2810 .amount = share(30'000'000)});
2811 tx[sfDestination] = charlie.human();
2816 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2817 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2819 env.balance(vaultAccount(keylet), issue) == asset(0));
2821 env(vault.del({.owner = owner, .id = keylet.key}));
2824 CaseArgs{.transferRate = 1.25});
2828 Account
const& owner,
2829 Account
const& issuer,
2830 Account
const& charlie,
2835 testcase(
"IOU frozen trust line to depositor");
2837 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2842 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2846 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
2847 auto tx = vault.withdraw(
2848 {.depositor = owner,
2850 .amount = asset(10)});
2851 tx[sfDestination] = charlie.human();
2854 env(withdrawToCharlie);
2861 auto const withdraw = vault.withdraw(
2862 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2871 auto tx = vault.deposit(
2872 {.depositor = owner,
2874 .amount = asset(10)});
2880 auto tx = vault.clawback(
2884 .amount = asset(0)});
2889 env(vault.del({.owner = owner, .id = keylet.key}));
2895 Account
const& owner,
2896 Account
const& issuer,
2897 Account
const& charlie,
2902 testcase(
"IOU no trust line to 3rd party");
2904 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2909 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2912 Account
const erin{
"erin"};
2913 env.fund(XRP(1000), erin);
2918 auto tx = vault.withdraw(
2919 {.depositor = owner,
2921 .amount = asset(10)});
2922 tx[sfDestination] = erin.human();
2930 Account
const& owner,
2931 Account
const& issuer,
2932 Account
const& charlie,
2937 testcase(
"IOU no trust line to depositor");
2939 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2944 env.trust(asset(0), owner);
2948 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2953 BEAST_EXPECT(trustline ==
nullptr);
2957 auto tx = vault.withdraw(
2958 {.depositor = owner,
2960 .amount = asset(10)});
2969 Account
const& owner,
2970 Account
const& issuer,
2971 Account
const& charlie,
2979 vault.create({.owner = owner, .asset = asset});
2986 env(trust(issuer, vaultAccount(keylet)[
"IOU"],
tfSetNoRipple),
2991 auto tx = vault.deposit(
2992 {.depositor = charlie,
2994 .amount = asset(100)});
3001 auto tx1 = vault.deposit(
3002 {.depositor = owner,
3004 .amount = asset(100)});
3009 auto tx2 = vault.withdraw(
3010 {.depositor = owner,
3012 .amount = shares(100)});
3013 tx2[sfDestination] = charlie.human();
3020 tx[sfAccount] = charlie.human();
3021 tx[sfMPTokenIssuanceID] =
3023 tx[sfTransactionType] = jss::MPTokenAuthorize;
3027 env(pay(owner, charlie, shares(100)), THISLINE);
3031 auto tx3 = vault.withdraw(
3032 {.depositor = charlie,
3034 .amount = shares(100)});
3038 env(pay(charlie, owner, shares(100)), THISLINE);
3042 tx = vault.withdraw(
3043 {.depositor = owner,
3045 .amount = asset(100)});
3050 env(vault.del({.owner = owner, .id = keylet.key}), THISLINE);
3052 {.charlieRipple =
false});
3057 Account
const& owner,
3058 Account
const& issuer,
3059 Account
const& charlie,
3060 auto const& vaultAccount,
3064 testcase(
"IOU calculation rounding");
3067 vault.create({.owner = owner, .asset = asset});
3072 auto const startingOwnerBalance = env.balance(owner, asset);
3074 (startingOwnerBalance.value() ==
3082 {.depositor = owner,
3084 .amount = asset(100)}));
3086 auto const tx1 = vault.deposit(
3087 {.depositor = owner,
3089 .amount = asset(
Number(375, -2))});
3090 for (
auto i = 0; i < 5; ++i)
3097 STAmount const xfer{asset, 1185, -1};
3099 env.balance(owner, asset) ==
3100 startingOwnerBalance.value() - xfer);
3102 env.balance(vaultAccount(keylet), asset) == xfer);
3104 auto const vault = env.le(keylet);
3105 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
3106 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
3113 {.depositor = owner,
3115 .amount = asset(Number(1000 + 37 * 5, -1))}));
3119 env.balance(owner, asset) ==
3120 startingOwnerBalance.value());
3122 env.balance(vaultAccount(keylet), asset) ==
3124 auto const vault = env.le(keylet);
3125 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
3126 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
3129 env(vault.del({.owner = owner, .id = keylet.key}));
3132 {.initialIOU =
Number(11875, -2)});
3135 Env env{*
this, testable_amendments()};
3137 env.current()->fees().accountReserve(0).drops() /
3139 env.current()->fees().increment.drops() /
3146 Account
const& owner,
3147 Account
const& issuer,
3148 Account
const& charlie,
3153 testcase(
"IOU no trust line to depositor no reserve");
3155 vault.create({.owner = owner, .asset = asset});
3161 env.trust(asset(0), owner);
3165 {.depositor = owner,
3167 .amount = asset(200)}));
3172 BEAST_EXPECT(trustline ==
nullptr);
3174 env(ticket::create(owner, 1));
3178 tx = vault.withdraw(
3179 {.depositor = owner,
3181 .amount = asset(10)});
3185 env(pay(charlie, owner, XRP(incReserve)));
3192 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
3197 Account
const& owner,
3198 Account
const& issuer,
3199 Account
const& charlie,
3204 testcase(
"IOU no reserve for share MPToken");
3206 vault.create({.owner = owner, .asset = asset});
3210 env(pay(owner, charlie, asset(100)));
3213 env(ticket::create(charlie, 3));
3218 {.depositor = charlie,
3220 .amount = asset(100)});
3224 env(pay(issuer, charlie, XRP(incReserve)));
3231 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
3235 Account
const& owner,
3236 Account
const& issuer,
3237 Account
const& charlie,
3242 testcase(
"IOU frozen trust line to 3rd party");
3244 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3249 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3253 auto const withdrawToCharlie = [&](
xrpl::Keylet keylet) {
3254 auto tx = vault.withdraw(
3255 {.depositor = owner,
3257 .amount = asset(10)});
3258 tx[sfDestination] = charlie.human();
3261 env(withdrawToCharlie);
3264 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
3268 auto const withdraw = vault.withdraw(
3269 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
3281 .amount = asset(0)}));
3284 env(vault.del({.owner = owner, .id = keylet.key}));
3290 Account
const& owner,
3291 Account
const& issuer,
3292 Account
const& charlie,
3299 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3304 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3312 auto tx = vault.withdraw(
3313 {.depositor = owner,
3315 .amount = asset(10)});
3319 tx[sfDestination] = charlie.human();
3325 {.depositor = owner,
3327 .amount = asset(10)});
3337 .amount = asset(0)}));
3340 env(vault.del({.owner = owner, .id = keylet.key}));
3348 using namespace test::jtx;
3352 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3353 Account issuer{
"issuer"};
3354 Account owner{
"owner"};
3355 Account depositor{
"depositor"};
3356 Account charlie{
"charlie"};
3357 Account pdOwner{
"pdOwner"};
3358 Account credIssuer1{
"credIssuer1"};
3359 Account credIssuer2{
"credIssuer2"};
3377 env.trust(asset(1000), owner);
3378 env(pay(issuer, owner, asset(500)));
3379 env.trust(asset(1000), depositor);
3380 env(pay(issuer, depositor, asset(500)));
3381 env.trust(asset(1000), charlie);
3382 env(pay(issuer, charlie, asset(5)));
3385 auto [tx, keylet] = vault.create(
3389 BEAST_EXPECT(env.le(keylet));
3392 testcase(
"private vault owner can deposit");
3393 auto tx = vault.deposit(
3394 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
3399 testcase(
"private vault depositor not authorized yet");
3400 auto tx = vault.deposit(
3401 {.depositor = depositor,
3403 .amount = asset(50)});
3408 testcase(
"private vault cannot set non-existing domain");
3409 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3415 testcase(
"private vault set domainId");
3418 pdomain::Credentials
const credentials1{
3419 {.issuer = credIssuer1, .credType = credType}};
3421 env(pdomain::setTx(pdOwner, credentials1));
3422 auto const domainId1 = [&]() {
3424 return pdomain::getNewDomain(env.meta());
3427 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3438 pdomain::Credentials
const credentials{
3439 {.issuer = credIssuer1, .credType = credType},
3440 {.issuer = credIssuer2, .credType = credType}};
3442 env(pdomain::setTx(pdOwner, credentials));
3443 auto const domainId = [&]() {
3445 return pdomain::getNewDomain(env.meta());
3448 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3454 tx = vault.set({.owner = owner, .id = keylet.
key});
3462 testcase(
"private vault depositor still not authorized");
3463 auto tx = vault.deposit(
3464 {.depositor = depositor,
3466 .amount = asset(50)});
3471 auto const credKeylet =
3472 credentials::keylet(depositor, credIssuer1, credType);
3474 testcase(
"private vault depositor now authorized");
3475 env(credentials::create(depositor, credIssuer1, credType));
3476 env(credentials::accept(depositor, credIssuer1, credType));
3477 env(credentials::create(charlie, credIssuer1, credType));
3480 auto credSle = env.le(credKeylet);
3481 BEAST_EXPECT(credSle !=
nullptr);
3483 auto tx = vault.deposit(
3484 {.depositor = depositor,
3486 .amount = asset(50)});
3491 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
3497 testcase(
"private vault depositor lost authorization");
3498 env(credentials::deleteCred(
3499 credIssuer1, depositor, credIssuer1, credType));
3500 env(credentials::deleteCred(
3501 credIssuer1, charlie, credIssuer1, credType));
3503 auto credSle = env.le(credKeylet);
3504 BEAST_EXPECT(credSle ==
nullptr);
3506 auto tx = vault.deposit(
3507 {.depositor = depositor,
3509 .amount = asset(50)});
3514 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
3515 auto const vault = env.le(keylet);
3516 BEAST_EXPECT(vault !=
nullptr);
3517 return MPTIssue(vault->at(sfShareMPTID));
3521 testcase(
"private vault expired authorization");
3522 uint32_t
const closeTime = env.current()
3524 .parentCloseTime.time_since_epoch()
3528 credentials::create(depositor, credIssuer2, credType);
3529 tx0[sfExpiration] = closeTime + 20;
3531 tx0 = credentials::create(charlie, credIssuer2, credType);
3532 tx0[sfExpiration] = closeTime + 20;
3536 env(credentials::accept(depositor, credIssuer2, credType));
3537 env(credentials::accept(charlie, credIssuer2, credType));
3542 auto tx1 = vault.deposit(
3543 {.depositor = depositor,
3545 .amount = asset(50)});
3551 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3560 auto const credsKeylet =
3561 credentials::keylet(depositor, credIssuer2, credType);
3562 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3564 auto tx2 = vault.deposit(
3565 {.depositor = depositor,
3567 .amount = asset(1)});
3571 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3575 auto const credsKeylet =
3576 credentials::keylet(charlie, credIssuer2, credType);
3577 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3580 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3582 auto tx3 = vault.deposit(
3583 {.depositor = charlie,
3585 .amount = asset(2)});
3589 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3590 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3595 testcase(
"private vault reset domainId");
3596 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3597 tx[sfDomainID] =
"0";
3602 {.depositor = depositor,
3604 .amount = asset(50)});
3608 tx = vault.withdraw(
3609 {.depositor = depositor,
3611 .amount = asset(50)});
3615 tx = vault.clawback(
3618 .holder = depositor,
3619 .amount = asset(0)});
3622 tx = vault.clawback(
3626 .amount = asset(0)});
3775 using namespace test::jtx;
3779 Account
const& owner;
3780 Account
const& issuer;
3781 Account
const& depositor;
3782 Account
const& vaultAccount;
3792 auto testCase = [&,
this](
3795 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3796 Account
const owner{
"owner"};
3797 Account
const issuer{
"issuer"};
3798 Account
const depositor{
"depositor"};
3800 env.fund(XRP(1000), issuer, owner, depositor);
3805 env.trust(asset(1000), owner);
3806 env.trust(asset(1000), depositor);
3807 env(pay(issuer, owner, asset(200)));
3808 env(pay(issuer, depositor, asset(200)));
3811 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3812 tx[sfScale] = scale;
3815 auto const [vaultAccount, issuanceId] =
3817 auto const vault = env.le(keylet);
3819 Account(
"vault", vault->at(sfAccount)),
3820 vault->at(sfShareMPTID)};
3823 env.memoize(vaultAccount);
3827 return env.app().openLedger().modify(
3831 if (!BEAST_EXPECT(vault !=
nullptr))
3833 auto shares = sb.
peek(
3835 if (!BEAST_EXPECT(shares !=
nullptr))
3837 if (fn(*vault, *shares))
3852 .depositor = depositor,
3853 .vaultAccount = vaultAccount,
3863 testCase(18, [&,
this](Env& env, Data d) {
3864 testcase(
"Scale deposit overflow on first deposit");
3865 auto tx = d.vault.deposit(
3866 {.depositor = d.depositor,
3868 .amount = d.asset(10)});
3873 testCase(18, [&,
this](Env& env, Data d) {
3874 testcase(
"Scale deposit overflow on second deposit");
3877 auto tx = d.vault.deposit(
3878 {.depositor = d.depositor,
3880 .amount = d.asset(5)});
3886 auto tx = d.vault.deposit(
3887 {.depositor = d.depositor,
3889 .amount = d.asset(10)});
3895 testCase(18, [&,
this](Env& env, Data d) {
3896 testcase(
"Scale deposit overflow on total shares");
3899 auto tx = d.vault.deposit(
3900 {.depositor = d.depositor,
3902 .amount = d.asset(5)});
3908 auto tx = d.vault.deposit(
3909 {.depositor = d.depositor,
3911 .amount = d.asset(5)});
3917 testCase(1, [&,
this](Env& env, Data d) {
3920 auto const start = env.balance(d.depositor, d.assets).number();
3921 auto tx = d.vault.deposit(
3922 {.depositor = d.depositor,
3924 .amount = d.asset(1)});
3927 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3929 env.balance(d.depositor, d.assets) ==
3933 testCase(1, [&,
this](Env& env, Data d) {
3934 testcase(
"Scale deposit insignificant amount");
3936 auto tx = d.vault.deposit(
3937 {.depositor = d.depositor,
3943 testCase(1, [&,
this](Env& env, Data d) {
3944 testcase(
"Scale deposit exact, using full precision");
3946 auto const start = env.balance(d.depositor, d.assets).number();
3947 auto tx = d.vault.deposit(
3948 {.depositor = d.depositor,
3953 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3955 env.balance(d.depositor, d.assets) ==
3959 testCase(1, [&,
this](Env& env, Data d) {
3960 testcase(
"Scale deposit exact, truncating from .5");
3962 auto const start = env.balance(d.depositor, d.assets).number();
3966 auto tx = d.vault.deposit(
3967 {.depositor = d.depositor,
3972 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3974 env.balance(d.depositor, d.assets) ==
3979 auto tx = d.vault.deposit(
3980 {.depositor = d.depositor,
3985 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3987 env.balance(d.depositor, d.assets) ==
3992 auto tx = d.vault.deposit(
3993 {.depositor = d.depositor,
3998 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
4000 env.balance(d.depositor, d.assets) ==
4005 testCase(1, [&,
this](Env& env, Data d) {
4006 testcase(
"Scale deposit exact, truncating from .01");
4008 auto const start = env.balance(d.depositor, d.assets).number();
4010 auto tx = d.vault.deposit(
4011 {.depositor = d.depositor,
4016 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
4018 env.balance(d.depositor, d.assets) ==
4023 auto tx = d.vault.deposit(
4024 {.depositor = d.depositor,
4029 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
4031 env.balance(d.depositor, d.assets) ==
4036 testCase(1, [&,
this](Env& env, Data d) {
4037 testcase(
"Scale deposit exact, truncating from .99");
4039 auto const start = env.balance(d.depositor, d.assets).number();
4041 auto tx = d.vault.deposit(
4042 {.depositor = d.depositor,
4047 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
4049 env.balance(d.depositor, d.assets) ==
4054 auto tx = d.vault.deposit(
4055 {.depositor = d.depositor,
4060 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
4062 env.balance(d.depositor, d.assets) ==
4067 testCase(1, [&,
this](Env& env, Data d) {
4069 auto const start = env.balance(d.depositor, d.assets).number();
4070 auto tx = d.vault.deposit(
4071 {.depositor = d.depositor,
4076 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4078 env.balance(d.depositor, d.assets) ==
4081 env.balance(d.vaultAccount, d.assets) ==
4084 env.balance(d.vaultAccount, d.shares) ==
4093 auto const start = env.balance(d.depositor, d.assets).number();
4094 auto tx = d.vault.withdraw(
4095 {.depositor = d.depositor,
4101 env.balance(d.depositor, d.shares) == d.share(900));
4103 env.balance(d.depositor, d.assets) ==
4106 env.balance(d.vaultAccount, d.assets) ==
4109 env.balance(d.vaultAccount, d.shares) ==
4114 testcase(
"Scale redeem with rounding");
4119 auto const start = env.balance(d.depositor, d.assets).number();
4120 d.peek([](
SLE& vault,
auto&) ->
bool {
4121 vault[sfAssetsAvailable] =
Number(1);
4129 auto tx = d.vault.withdraw(
4130 {.depositor = d.depositor,
4136 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4138 env.balance(d.depositor, d.assets) ==
4141 env.balance(d.vaultAccount, d.assets) ==
4144 env.balance(d.vaultAccount, d.shares) ==
4154 auto const start = env.balance(d.depositor, d.assets).number();
4156 tx = d.vault.withdraw(
4157 {.depositor = d.depositor,
4163 env.balance(d.depositor, d.shares) == d.share(875 - 21));
4165 env.balance(d.depositor, d.assets) ==
4168 env.balance(d.vaultAccount, d.assets) ==
4171 env.balance(d.vaultAccount, d.shares) ==
4177 auto const rest = env.balance(d.depositor, d.shares).number();
4179 tx = d.vault.withdraw(
4180 {.depositor = d.depositor,
4182 .amount =
STAmount(d.share, rest)});
4185 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4187 env.balance(d.vaultAccount, d.assets).number() == 0);
4189 env.balance(d.vaultAccount, d.shares).number() == 0);
4193 testCase(18, [&,
this](Env& env, Data d) {
4194 testcase(
"Scale withdraw overflow");
4197 auto tx = d.vault.deposit(
4198 {.depositor = d.depositor,
4200 .amount = d.asset(5)});
4206 auto tx = d.vault.withdraw(
4207 {.depositor = d.depositor,
4215 testCase(1, [&,
this](Env& env, Data d) {
4217 auto const start = env.balance(d.depositor, d.assets).number();
4218 auto tx = d.vault.deposit(
4219 {.depositor = d.depositor,
4224 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4226 env.balance(d.depositor, d.assets) ==
4229 env.balance(d.vaultAccount, d.assets) ==
4232 env.balance(d.vaultAccount, d.shares) ==
4244 auto const start = env.balance(d.depositor, d.assets).number();
4245 auto tx = d.vault.withdraw(
4246 {.depositor = d.depositor,
4252 env.balance(d.depositor, d.shares) == d.share(900));
4254 env.balance(d.depositor, d.assets) ==
4257 env.balance(d.vaultAccount, d.assets) ==
4260 env.balance(d.vaultAccount, d.shares) ==
4265 testcase(
"Scale withdraw insignificant amount");
4266 auto tx = d.vault.withdraw(
4267 {.depositor = d.depositor,
4274 testcase(
"Scale withdraw with rounding assets");
4282 auto const start = env.balance(d.depositor, d.assets).number();
4283 d.peek([](
SLE& vault,
auto&) ->
bool {
4284 vault[sfAssetsAvailable] =
Number(1);
4292 auto tx = d.vault.withdraw(
4293 {.depositor = d.depositor,
4299 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4301 env.balance(d.depositor, d.assets) ==
4304 env.balance(d.vaultAccount, d.assets) ==
4307 env.balance(d.vaultAccount, d.shares) ==
4312 testcase(
"Scale withdraw with rounding shares up");
4320 auto const start = env.balance(d.depositor, d.assets).number();
4321 auto tx = d.vault.withdraw(
4322 {.depositor = d.depositor,
4328 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4330 env.balance(d.depositor, d.assets) ==
4333 env.balance(d.vaultAccount, d.assets) ==
4336 env.balance(d.vaultAccount, d.shares) ==
4341 testcase(
"Scale withdraw with rounding shares down");
4349 auto const start = env.balance(d.depositor, d.assets).number();
4350 auto tx = d.vault.withdraw(
4351 {.depositor = d.depositor,
4357 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4359 env.balance(d.depositor, d.assets) ==
4362 env.balance(d.vaultAccount, d.assets) ==
4365 env.balance(d.vaultAccount, d.shares) ==
4370 testcase(
"Scale withdraw tiny amount");
4372 auto const start = env.balance(d.depositor, d.assets).number();
4373 auto tx = d.vault.withdraw(
4374 {.depositor = d.depositor,
4380 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4382 env.balance(d.depositor, d.assets) ==
4385 env.balance(d.vaultAccount, d.assets) ==
4388 env.balance(d.vaultAccount, d.shares) ==
4395 env.balance(d.vaultAccount, d.assets).number();
4397 tx = d.vault.withdraw(
4398 {.depositor = d.depositor,
4400 .amount =
STAmount(d.asset, rest)});
4403 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4405 env.balance(d.vaultAccount, d.assets).number() == 0);
4407 env.balance(d.vaultAccount, d.shares).number() == 0);
4411 testCase(18, [&,
this](Env& env, Data d) {
4412 testcase(
"Scale clawback overflow");
4415 auto tx = d.vault.deposit(
4416 {.depositor = d.depositor,
4418 .amount = d.asset(5)});
4424 auto tx = d.vault.clawback(
4425 {.issuer = d.issuer,
4427 .holder = d.depositor,
4434 testCase(1, [&,
this](Env& env, Data d) {
4436 auto const start = env.balance(d.depositor, d.assets).number();
4437 auto tx = d.vault.deposit(
4438 {.depositor = d.depositor,
4443 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4445 env.balance(d.depositor, d.assets) ==
4448 env.balance(d.vaultAccount, d.assets) ==
4451 env.balance(d.vaultAccount, d.shares) ==
4462 auto const start = env.balance(d.depositor, d.assets).number();
4463 auto tx = d.vault.clawback(
4464 {.issuer = d.issuer,
4466 .holder = d.depositor,
4471 env.balance(d.depositor, d.shares) == d.share(900));
4473 env.balance(d.depositor, d.assets) ==
4476 env.balance(d.vaultAccount, d.assets) ==
4479 env.balance(d.vaultAccount, d.shares) ==
4484 testcase(
"Scale clawback insignificant amount");
4485 auto tx = d.vault.clawback(
4486 {.issuer = d.issuer,
4488 .holder = d.depositor,
4494 testcase(
"Scale clawback with rounding assets");
4502 auto const start = env.balance(d.depositor, d.assets).number();
4503 auto tx = d.vault.clawback(
4504 {.issuer = d.issuer,
4506 .holder = d.depositor,
4511 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4513 env.balance(d.depositor, d.assets) ==
4516 env.balance(d.vaultAccount, d.assets) ==
4519 env.balance(d.vaultAccount, d.shares) ==
4524 testcase(
"Scale clawback with rounding shares up");
4532 auto const start = env.balance(d.depositor, d.assets).number();
4533 auto tx = d.vault.clawback(
4534 {.issuer = d.issuer,
4536 .holder = d.depositor,
4541 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4543 env.balance(d.depositor, d.assets) ==
4546 env.balance(d.vaultAccount, d.assets) ==
4549 env.balance(d.vaultAccount, d.shares) ==
4554 testcase(
"Scale clawback with rounding shares down");
4562 auto const start = env.balance(d.depositor, d.assets).number();
4563 auto tx = d.vault.clawback(
4564 {.issuer = d.issuer,
4566 .holder = d.depositor,
4571 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4573 env.balance(d.depositor, d.assets) ==
4576 env.balance(d.vaultAccount, d.assets) ==
4579 env.balance(d.vaultAccount, d.shares) ==
4584 testcase(
"Scale clawback tiny amount");
4586 auto const start = env.balance(d.depositor, d.assets).number();
4587 auto tx = d.vault.clawback(
4588 {.issuer = d.issuer,
4590 .holder = d.depositor,
4595 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4597 env.balance(d.depositor, d.assets) ==
4600 env.balance(d.vaultAccount, d.assets) ==
4603 env.balance(d.vaultAccount, d.shares) ==
4610 env.balance(d.vaultAccount, d.assets).number();
4611 d.peek([](
SLE& vault,
auto&) ->
bool {
4612 vault[sfAssetsAvailable] =
Number(5);
4620 tx = d.vault.clawback(
4621 {.issuer = d.issuer,
4623 .holder = d.depositor,
4624 .amount =
STAmount(d.asset, rest)});
4627 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4629 env.balance(d.vaultAccount, d.assets).number() == 0);
4631 env.balance(d.vaultAccount, d.shares).number() == 0);
4639 using namespace test::jtx;
4642 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4643 Account
const owner{
"owner"};
4644 Account
const issuer{
"issuer"};
4646 env.fund(XRP(1000), issuer, owner);
4650 env.trust(asset(1000), owner);
4651 env(pay(issuer, owner, asset(200)));
4654 auto const sequence = env.seq(owner);
4655 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4661 auto tx1 = vault.deposit(
4662 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4665 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4666 tx2[sfAssetsMaximum] = asset(1000).number();
4671 auto const sleVault = [&env, keylet = keylet,
this]() {
4672 auto const vault = env.le(keylet);
4673 BEAST_EXPECT(vault !=
nullptr);
4677 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4680 BEAST_EXPECT(vault.isObject());
4682 constexpr auto checkString =
4684 return node.isMember(field.fieldName) &&
4685 node[field.fieldName].isString() &&
4686 node[field.fieldName] == v;
4688 constexpr auto checkObject =
4690 return node.isMember(field.fieldName) &&
4691 node[field.fieldName].isObject() &&
4692 node[field.fieldName] == v;
4694 constexpr auto checkInt =
4695 [](
auto& node,
SField const& field,
int v) ->
bool {
4696 return node.isMember(field.fieldName) &&
4697 ((node[field.fieldName].isInt() &&
4698 node[field.fieldName] ==
Json::Int(v)) ||
4699 (node[field.fieldName].isUInt() &&
4703 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4704 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4705 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4709 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4711 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4712 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4713 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4714 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4715 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
4717 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4718 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4719 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4720 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4721 BEAST_EXPECT(checkInt(
4724 if (issuance.isObject())
4727 issuance[
"LedgerEntryType"].asString() ==
4730 issuance[jss::mpt_issuance_id].asString() == strShareID);
4731 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4732 BEAST_EXPECT(checkInt(
4737 checkString(issuance, sfOutstandingAmount,
"50000000"));
4742 testcase(
"RPC ledger_entry selected by key");
4744 jvParams[jss::ledger_index] = jss::validated;
4745 jvParams[jss::vault] =
strHex(keylet.
key);
4746 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4748 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4749 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4750 check(jvVault[jss::result][jss::node]);
4754 testcase(
"RPC ledger_entry selected by owner and seq");
4756 jvParams[jss::ledger_index] = jss::validated;
4757 jvParams[jss::vault][jss::owner] = owner.human();
4758 jvParams[jss::vault][jss::seq] = sequence;
4759 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4761 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4762 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4763 check(jvVault[jss::result][jss::node]);
4767 testcase(
"RPC ledger_entry cannot find vault by key");
4769 jvParams[jss::ledger_index] = jss::validated;
4771 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4773 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4777 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4779 jvParams[jss::ledger_index] = jss::validated;
4780 jvParams[jss::vault][jss::owner] = issuer.human();
4781 jvParams[jss::vault][jss::seq] = 1'000'000;
4782 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4784 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4788 testcase(
"RPC ledger_entry malformed key");
4790 jvParams[jss::ledger_index] = jss::validated;
4791 jvParams[jss::vault] = 42;
4792 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4794 jvVault[jss::result][jss::error].asString() ==
4795 "malformedRequest");
4799 testcase(
"RPC ledger_entry malformed owner");
4801 jvParams[jss::ledger_index] = jss::validated;
4802 jvParams[jss::vault][jss::owner] = 42;
4803 jvParams[jss::vault][jss::seq] = sequence;
4804 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4806 jvVault[jss::result][jss::error].asString() ==
4811 testcase(
"RPC ledger_entry malformed seq");
4813 jvParams[jss::ledger_index] = jss::validated;
4814 jvParams[jss::vault][jss::owner] = issuer.human();
4815 jvParams[jss::vault][jss::seq] =
"foo";
4816 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4818 jvVault[jss::result][jss::error].asString() ==
4819 "malformedRequest");
4823 testcase(
"RPC ledger_entry negative seq");
4825 jvParams[jss::ledger_index] = jss::validated;
4826 jvParams[jss::vault][jss::owner] = issuer.human();
4827 jvParams[jss::vault][jss::seq] = -1;
4828 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4830 jvVault[jss::result][jss::error].asString() ==
4831 "malformedRequest");
4835 testcase(
"RPC ledger_entry oversized seq");
4837 jvParams[jss::ledger_index] = jss::validated;
4838 jvParams[jss::vault][jss::owner] = issuer.human();
4839 jvParams[jss::vault][jss::seq] = 1e20;
4840 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4842 jvVault[jss::result][jss::error].asString() ==
4843 "malformedRequest");
4847 testcase(
"RPC ledger_entry bool seq");
4849 jvParams[jss::ledger_index] = jss::validated;
4850 jvParams[jss::vault][jss::owner] = issuer.human();
4851 jvParams[jss::vault][jss::seq] =
true;
4852 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4854 jvVault[jss::result][jss::error].asString() ==
4855 "malformedRequest");
4862 jvParams[jss::account] = owner.human();
4863 jvParams[jss::type] = jss::vault;
4865 "json",
"account_objects",
to_string(jvParams))[jss::result];
4867 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4868 check(jv[jss::account_objects][0u]);
4875 jvParams[jss::ledger_index] = jss::validated;
4876 jvParams[jss::binary] =
false;
4877 jvParams[jss::type] = jss::vault;
4879 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4880 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4881 check(jv[jss::result][jss::state][0u]);
4885 testcase(
"RPC vault_info command line");
4887 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4889 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4890 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4892 jv[jss::result][jss::vault],
4893 jv[jss::result][jss::vault][jss::shares]);
4899 jvParams[jss::ledger_index] = jss::validated;
4900 jvParams[jss::vault_id] =
strHex(keylet.
key);
4901 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4903 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4904 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4906 jv[jss::result][jss::vault],
4907 jv[jss::result][jss::vault][jss::shares]);
4911 testcase(
"RPC vault_info invalid vault_id");
4913 jvParams[jss::ledger_index] = jss::validated;
4914 jvParams[jss::vault_id] =
"foobar";
4915 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4917 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4921 testcase(
"RPC vault_info json invalid index");
4923 jvParams[jss::ledger_index] = jss::validated;
4924 jvParams[jss::vault_id] = 0;
4925 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4927 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4931 testcase(
"RPC vault_info json by owner and sequence");
4933 jvParams[jss::ledger_index] = jss::validated;
4934 jvParams[jss::owner] = owner.human();
4935 jvParams[jss::seq] = sequence;
4936 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4938 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4939 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4941 jv[jss::result][jss::vault],
4942 jv[jss::result][jss::vault][jss::shares]);
4946 testcase(
"RPC vault_info json malformed sequence");
4948 jvParams[jss::ledger_index] = jss::validated;
4949 jvParams[jss::owner] = owner.human();
4950 jvParams[jss::seq] =
"foobar";
4951 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4953 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4957 testcase(
"RPC vault_info json invalid sequence");
4959 jvParams[jss::ledger_index] = jss::validated;
4960 jvParams[jss::owner] = owner.human();
4961 jvParams[jss::seq] = 0;
4962 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4964 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4968 testcase(
"RPC vault_info json negative sequence");
4970 jvParams[jss::ledger_index] = jss::validated;
4971 jvParams[jss::owner] = owner.human();
4972 jvParams[jss::seq] = -1;
4973 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4975 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4979 testcase(
"RPC vault_info json oversized sequence");
4981 jvParams[jss::ledger_index] = jss::validated;
4982 jvParams[jss::owner] = owner.human();
4983 jvParams[jss::seq] = 1e20;
4984 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4986 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4990 testcase(
"RPC vault_info json bool sequence");
4992 jvParams[jss::ledger_index] = jss::validated;
4993 jvParams[jss::owner] = owner.human();
4994 jvParams[jss::seq] =
true;
4995 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4997 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5001 testcase(
"RPC vault_info json malformed owner");
5003 jvParams[jss::ledger_index] = jss::validated;
5004 jvParams[jss::owner] =
"foobar";
5005 jvParams[jss::seq] = sequence;
5006 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5008 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5012 testcase(
"RPC vault_info json invalid combination only owner");
5014 jvParams[jss::ledger_index] = jss::validated;
5015 jvParams[jss::owner] = owner.human();
5016 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5018 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5022 testcase(
"RPC vault_info json invalid combination only seq");
5024 jvParams[jss::ledger_index] = jss::validated;
5025 jvParams[jss::seq] = sequence;
5026 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5028 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5032 testcase(
"RPC vault_info json invalid combination seq vault_id");
5034 jvParams[jss::ledger_index] = jss::validated;
5035 jvParams[jss::vault_id] =
strHex(keylet.
key);
5036 jvParams[jss::seq] = sequence;
5037 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5039 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5043 testcase(
"RPC vault_info json invalid combination owner vault_id");
5045 jvParams[jss::ledger_index] = jss::validated;
5046 jvParams[jss::vault_id] =
strHex(keylet.
key);
5047 jvParams[jss::owner] = owner.human();
5048 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5050 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5055 "RPC vault_info json invalid combination owner seq "
5058 jvParams[jss::ledger_index] = jss::validated;
5059 jvParams[jss::vault_id] =
strHex(keylet.
key);
5060 jvParams[jss::seq] = sequence;
5061 jvParams[jss::owner] = owner.human();
5062 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5064 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5068 testcase(
"RPC vault_info json no input");
5070 jvParams[jss::ledger_index] = jss::validated;
5071 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
5073 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5077 testcase(
"RPC vault_info command line invalid index");
5078 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
5079 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
5083 testcase(
"RPC vault_info command line invalid index");
5084 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
5086 jv[jss::result][jss::error].asString() ==
"malformedRequest");
5090 testcase(
"RPC vault_info command line invalid index");
5094 jv[jss::result][jss::error].asString() ==
"entryNotFound");
5098 testcase(
"RPC vault_info command line invalid ledger");
5101 jv[jss::result][jss::error].asString() ==
"lgrNotFound");
5108 using namespace test::jtx;
5110 Env env(*
this, testable_amendments());
5111 Account alice{
"alice"};
5113 Account carol{
"carol"};
5120 auto const xrpBalance =
5124 if (BEAST_EXPECT(sle !=
nullptr))
5125 return sle->getFieldAmount(sfBalance).xrp().drops();
5129 auto testCase = [&,
this](
auto test, CaseArgs args = {}) {
5130 Env env{*
this, testable_amendments() | featureSingleAssetVault};
5135 env.fund(XRP(10000), alice);
5136 env.fund(XRP(20000), bob);
5137 env.fund(XRP(30000), carol);
5151 test(env, vault, args.asset);
5154 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5155 testcase(
"delegated vault creation");
5156 auto startBalance = xrpBalance(env, carol);
5157 if (!BEAST_EXPECT(startBalance.has_value()))
5160 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5161 env(tx, delegate::as(alice));
5163 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
5166 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5167 testcase(
"delegated deposit and withdrawal");
5168 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5172 auto const amount = 1513;
5173 auto const baseFee = env.current()->fees().base;
5175 auto startBalance = xrpBalance(env, carol);
5176 if (!BEAST_EXPECT(startBalance.has_value()))
5180 {.depositor = carol,
5182 .amount = asset(amount)});
5183 env(tx, delegate::as(alice));
5185 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
5187 tx = vault.withdraw(
5188 {.depositor = carol,
5190 .amount = asset(amount - 1)});
5191 env(tx, delegate::as(alice));
5193 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
5195 tx = vault.withdraw(
5196 {.depositor = carol, .id = keylet.
key, .amount = asset(1)});
5199 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5202 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5203 testcase(
"delegated withdrawal same as base fee and deletion");
5204 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5208 auto const amount = 25537;
5209 auto const baseFee = env.current()->fees().base;
5211 auto startBalance = xrpBalance(env, carol);
5212 if (!BEAST_EXPECT(startBalance.has_value()))
5216 {.depositor = carol,
5218 .amount = asset(amount)});
5222 xrpBalance(env, carol) == *startBalance - amount - baseFee);
5224 tx = vault.withdraw(
5225 {.depositor = carol,
5227 .amount = asset(baseFee)});
5228 env(tx, delegate::as(alice));
5230 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
5232 tx = vault.withdraw(
5233 {.depositor = carol,
5235 .amount = asset(amount - baseFee)});
5236 env(tx, delegate::as(alice));
5238 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5240 tx = vault.del({.owner = carol, .id = keylet.
key});
5241 env(tx, delegate::as(alice));