45 using namespace test::jtx;
46 Account issuer{
"issuer"};
47 Account owner{
"owner"};
48 Account depositor{
"depositor"};
49 Account charlie{
"charlie"};
52 auto const testSequence = [&,
this](
57 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
58 tx[sfData] =
"AFEED00E";
59 tx[sfAssetsMaximum] = asset(100).number();
62 BEAST_EXPECT(env.le(keylet));
65 auto const [share, vaultAccount] =
70 auto const vault = env.le(keylet);
71 BEAST_EXPECT(vault !=
nullptr);
72 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
73 BEAST_EXPECT(vault->at(sfScale) == 6);
75 BEAST_EXPECT(vault->at(sfScale) == 0);
78 BEAST_EXPECT(shares !=
nullptr);
79 if (asset.raw().holds<
Issue>() && !asset.raw().
native())
80 BEAST_EXPECT(shares->at(sfAssetScale) == 6);
82 BEAST_EXPECT(shares->at(sfAssetScale) == 0);
85 Account(
"vault", vault->at(sfAccount))};
87 auto const shares = share.raw().get<
MPTIssue>();
88 env.memoize(vaultAccount);
91 Account alice{
"alice"};
93 env.fund(XRP(1000), alice, erin);
98 testcase(prefix +
" fail to deposit more than assets held");
99 auto tx = vault.deposit(
100 {.depositor = depositor,
102 .amount = asset(10000)});
108 testcase(prefix +
" deposit non-zero amount");
109 auto tx = vault.deposit(
110 {.depositor = depositor,
112 .amount = asset(50)});
116 env.balance(depositor, shares) == share(50 * scale));
120 testcase(prefix +
" deposit non-zero amount again");
121 auto tx = vault.deposit(
122 {.depositor = depositor,
124 .amount = asset(50)});
128 env.balance(depositor, shares) == share(100 * scale));
132 testcase(prefix +
" fail to delete non-empty vault");
133 auto tx = vault.del({.owner = owner, .id = keylet.
key});
139 testcase(prefix +
" fail to update because wrong owner");
140 auto tx = vault.set({.owner = issuer, .id = keylet.
key});
141 tx[sfAssetsMaximum] = asset(50).number();
148 prefix +
" fail to set maximum lower than current amount");
149 auto tx = vault.set({.owner = owner, .id = keylet.
key});
150 tx[sfAssetsMaximum] = asset(50).number();
156 testcase(prefix +
" set maximum higher than current amount");
157 auto tx = vault.set({.owner = owner, .id = keylet.
key});
158 tx[sfAssetsMaximum] = asset(150).number();
164 testcase(prefix +
" set maximum is idempotent, set it again");
165 auto tx = vault.set({.owner = owner, .id = keylet.
key});
166 tx[sfAssetsMaximum] = asset(150).number();
173 auto tx = vault.set({.owner = owner, .id = keylet.
key});
180 testcase(prefix +
" fail to set domain on public vault");
181 auto tx = vault.set({.owner = owner, .id = keylet.
key});
188 testcase(prefix +
" fail to deposit more than maximum");
189 auto tx = vault.deposit(
190 {.depositor = depositor,
192 .amount = asset(100)});
198 testcase(prefix +
" reset maximum to zero i.e. not enforced");
199 auto tx = vault.set({.owner = owner, .id = keylet.
key});
200 tx[sfAssetsMaximum] = asset(0).number();
206 testcase(prefix +
" fail to withdraw more than assets held");
207 auto tx = vault.withdraw(
208 {.depositor = depositor,
210 .amount = asset(1000)});
216 testcase(prefix +
" deposit some more");
217 auto tx = vault.deposit(
218 {.depositor = depositor,
220 .amount = asset(100)});
224 env.balance(depositor, shares) == share(200 * scale));
228 testcase(prefix +
" clawback some");
231 auto tx = vault.clawback(
235 .amount = asset(10)});
238 if (!asset.raw().native())
241 env.balance(depositor, shares) == share(190 * scale));
249 auto tx = vault.clawback(
250 {.issuer = issuer, .id = keylet.
key, .holder = depositor});
253 if (!asset.raw().native())
255 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
258 auto tx = vault.clawback(
262 .amount = asset(10)});
268 auto tx = vault.withdraw(
269 {.depositor = depositor,
271 .amount = asset(10)});
278 if (!asset.raw().native())
280 testcase(prefix +
" deposit again");
281 auto tx = vault.deposit(
282 {.depositor = depositor,
284 .amount = asset(200)});
288 env.balance(depositor, shares) == share(200 * scale));
292 testcase(prefix +
" deposit/withdrawal same or less than fee");
293 auto const amount = env.current()->fees().base;
295 auto tx = vault.deposit(
296 {.depositor = depositor,
303 {.depositor = depositor,
310 {.depositor = depositor,
318 {.depositor = depositor,
321 tx[sfDestination] = charlie.human();
326 {.depositor = depositor,
328 .amount = amount - 1});
333 {.depositor = depositor,
335 .amount = amount - 1});
342 prefix +
" fail to withdraw to 3rd party lsfDepositAuth");
343 auto tx = vault.withdraw(
344 {.depositor = depositor,
346 .amount = asset(100)});
347 tx[sfDestination] = alice.human();
353 testcase(prefix +
" fail to withdraw to zero destination");
354 auto tx = vault.withdraw(
355 {.depositor = depositor,
357 .amount = asset(1000)});
358 tx[sfDestination] =
"0";
363 if (!asset.raw().native())
366 prefix +
" fail to withdraw to 3rd party no authorization");
367 auto tx = vault.withdraw(
368 {.depositor = depositor,
370 .amount = asset(100)});
371 tx[sfDestination] = erin.human();
380 " fail to withdraw to 3rd party lsfRequireDestTag");
381 auto tx = vault.withdraw(
382 {.depositor = depositor,
384 .amount = asset(100)});
385 tx[sfDestination] = dave.human();
391 testcase(prefix +
" withdraw to 3rd party lsfRequireDestTag");
392 auto tx = vault.withdraw(
393 {.depositor = depositor,
395 .amount = asset(50)});
396 tx[sfDestination] = dave.human();
397 tx[sfDestinationTag] =
"0";
403 testcase(prefix +
" deposit again");
404 auto tx = vault.deposit(
405 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
411 testcase(prefix +
" fail to withdraw lsfRequireDestTag");
412 auto tx = vault.withdraw(
413 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
419 testcase(prefix +
" withdraw with tag");
420 auto tx = vault.withdraw(
421 {.depositor = dave, .id = keylet.
key, .amount = asset(50)});
422 tx[sfDestinationTag] =
"0";
428 testcase(prefix +
" withdraw to authorized 3rd party");
429 auto tx = vault.withdraw(
430 {.depositor = depositor,
432 .amount = asset(50)});
433 tx[sfDestination] = charlie.human();
437 env.balance(depositor, shares) == share(100 * scale));
441 testcase(prefix +
" withdraw to issuer");
442 auto tx = vault.withdraw(
443 {.depositor = depositor,
445 .amount = asset(50)});
446 tx[sfDestination] = issuer.human();
450 env.balance(depositor, shares) == share(50 * scale));
453 if (!asset.raw().native())
455 testcase(prefix +
" issuer deposits");
456 auto tx = vault.deposit(
457 {.depositor = issuer,
459 .amount = asset(10)});
462 BEAST_EXPECT(env.balance(issuer, shares) == share(10 * scale));
464 testcase(prefix +
" issuer withdraws");
466 {.depositor = issuer,
468 .amount = share(10 * scale)});
471 BEAST_EXPECT(env.balance(issuer, shares) == share(0 * scale));
475 testcase(prefix +
" withdraw remaining assets");
476 auto tx = vault.withdraw(
477 {.depositor = depositor,
479 .amount = asset(50)});
482 BEAST_EXPECT(env.balance(depositor, shares) == share(0));
484 if (!asset.raw().native())
486 auto tx = vault.clawback(
490 .amount = asset(0)});
496 auto tx = vault.withdraw(
497 {.depositor = depositor,
499 .amount = share(10)});
505 if (!asset.raw().native() && asset.raw().holds<
Issue>())
507 testcase(prefix +
" temporary authorization for 3rd party");
508 env(trust(erin, asset(1000)));
509 env(trust(issuer, asset(0), erin,
tfSetfAuth));
510 env(pay(issuer, erin, asset(10)));
513 auto tx = vault.deposit(
514 {.depositor = erin, .id = keylet.
key, .amount = asset(10)});
518 auto tx = pay(erin, depositor, share(10 * scale));
526 {.depositor = depositor,
528 .amount = asset(1)}));
535 testcase(prefix +
" withdraw to authorized 3rd party");
538 {.depositor = depositor,
540 .amount = asset(10)});
541 tx[sfDestination] = erin.human();
546 env(pay(erin, issuer, asset(10)));
549 testcase(prefix +
" fail to pay to unauthorized 3rd party");
550 env(trust(erin, asset(0)));
554 env(pay(depositor, erin, share(1)), ter{
tecNO_LINE});
559 {.depositor = depositor,
561 .amount = asset(1)});
567 testcase(prefix +
" fail to delete because wrong owner");
568 auto tx = vault.del({.owner = issuer, .id = keylet.
key});
574 testcase(prefix +
" delete empty vault");
575 auto tx = vault.del({.owner = owner, .id = keylet.
key});
578 BEAST_EXPECT(!env.le(keylet));
582 auto testCases = [&,
this](
585 Env env{*
this, testable_amendments() | featureSingleAssetVault};
588 env.fund(XRP(1000), issuer, owner, depositor, charlie, dave);
598 testSequence(prefix, env, vault, asset);
605 testCases(
"IOU", [&](Env& env) ->
Asset {
607 env(trust(owner, asset(1000)));
608 env(trust(depositor, asset(1000)));
609 env(trust(charlie, asset(1000)));
610 env(trust(dave, asset(1000)));
611 env(trust(issuer, asset(0), owner,
tfSetfAuth));
612 env(trust(issuer, asset(0), depositor,
tfSetfAuth));
613 env(trust(issuer, asset(0), charlie,
tfSetfAuth));
614 env(trust(issuer, asset(0), dave,
tfSetfAuth));
615 env(pay(issuer, depositor, asset(1000)));
620 testCases(
"MPT", [&](Env& env) ->
Asset {
621 MPTTester mptt{env, issuer, mptInitNoFund};
625 mptt.authorize({.account = depositor});
626 mptt.authorize({.account = charlie});
627 mptt.authorize({.account = dave});
628 env(pay(issuer, depositor, asset(1000)));
637 using namespace test::jtx;
642 testable_amendments() | featureSingleAssetVault;
645 auto testCase = [&,
this](
648 Account
const& issuer,
649 Account
const& owner,
652 CaseArgs args = {}) {
653 Env env{*
this, args.features};
654 Account issuer{
"issuer"};
655 Account owner{
"owner"};
657 env.fund(XRP(1000), issuer, owner);
665 env(trust(owner, asset(1000)));
666 env(trust(issuer, asset(0), owner,
tfSetfAuth));
667 env(pay(issuer, owner, asset(1000)));
670 test(env, issuer, owner, asset, vault);
675 Account
const& issuer,
676 Account
const& owner,
679 testcase(
"disabled single asset vault");
682 vault.create({.owner = owner, .asset = asset});
686 auto tx = vault.set({.owner = owner, .id = keylet.
key});
691 auto tx = vault.deposit(
694 .amount = asset(10)});
699 auto tx = vault.withdraw(
702 .amount = asset(10)});
707 auto tx = vault.clawback(
711 .amount = asset(10)});
716 auto tx = vault.del({.owner = owner, .id = keylet.
key});
720 {.features = testable_amendments() - featureSingleAssetVault});
722 testCase([&](Env& env,
723 Account
const& issuer,
724 Account
const& owner,
729 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
734 auto tx = vault.set({.owner = owner, .id = keylet.
key});
740 auto tx = vault.deposit(
743 .amount = asset(10)});
749 auto tx = vault.withdraw(
752 .amount = asset(10)});
758 auto tx = vault.clawback(
762 .amount = asset(10)});
768 auto tx = vault.del({.owner = owner, .id = keylet.
key});
774 testCase([&](Env& env,
775 Account
const& issuer,
776 Account
const& owner,
781 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
786 auto tx = vault.set({.owner = owner, .id = keylet.
key});
792 auto tx = vault.deposit(
795 .amount = asset(10)});
801 auto tx = vault.withdraw(
804 .amount = asset(10)});
810 auto tx = vault.clawback(
814 .amount = asset(10)});
820 auto tx = vault.del({.owner = owner, .id = keylet.
key});
829 Account
const& owner,
832 testcase(
"disabled permissioned domain");
835 vault.create({.owner = owner, .asset =
xrpIssue()});
840 auto tx = vault.set({.owner = owner, .id = keylet.
key});
846 auto tx = vault.set({.owner = owner, .id = keylet.
key});
847 tx[sfDomainID] =
"0";
851 {.features = (testable_amendments() | featureSingleAssetVault) -
852 featurePermissionedDomains});
854 testCase([&](Env& env,
855 Account
const& issuer,
856 Account
const& owner,
862 vault.create({.owner = owner, .asset =
xrpIssue()});
865 auto tx = vault.set({
873 auto tx = vault.deposit(
876 .amount = asset(10)});
881 auto tx = vault.withdraw(
884 .amount = asset(10)});
889 auto tx = vault.clawback(
893 .amount = asset(10)});
898 auto tx = vault.del({
906 testCase([&](Env& env,
907 Account
const& issuer,
908 Account
const& owner,
913 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
916 auto tx = vault.clawback(
920 .amount = asset(10)});
925 testCase([&](Env& env,
927 Account
const& owner,
930 testcase(
"withdraw to bad destination");
932 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
935 auto tx = vault.withdraw(
938 .amount = asset(10)});
939 tx[jss::Destination] =
"0";
944 testCase([&](Env& env,
946 Account
const& owner,
953 vault.create({.owner = owner, .asset = asset});
960 vault.create({.owner = owner, .asset = asset});
968 vault.create({.owner = owner, .asset = asset});
972 auto const sleVault = env.le(keylet);
973 BEAST_EXPECT(sleVault);
974 BEAST_EXPECT((*sleVault)[sfScale] == 18);
979 vault.create({.owner = owner, .asset = asset});
983 auto const sleVault = env.le(keylet);
984 BEAST_EXPECT(sleVault);
985 BEAST_EXPECT((*sleVault)[sfScale] == 0);
990 vault.create({.owner = owner, .asset = asset});
993 auto const sleVault = env.le(keylet);
994 BEAST_EXPECT(sleVault);
995 BEAST_EXPECT((*sleVault)[sfScale] == 6);
999 testCase([&](Env& env,
1001 Account
const& owner,
1004 testcase(
"create or set invalid data");
1006 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1022 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1028 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1035 testCase([&](Env& env,
1037 Account
const& owner,
1042 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1045 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1050 testCase([&](Env& env,
1052 Account
const& owner,
1055 testcase(
"create with invalid metadata");
1057 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1061 tx[sfMPTokenMetadata] =
"";
1074 testCase([&](Env& env,
1076 Account
const& owner,
1081 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1084 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1090 testCase([&](Env& env,
1092 Account
const& owner,
1095 testcase(
"invalid deposit amount");
1097 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1100 auto tx = vault.deposit(
1101 {.depositor = owner,
1108 auto tx = vault.deposit(
1109 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1114 testCase([&](Env& env,
1116 Account
const& owner,
1119 testcase(
"invalid set immutable flag");
1121 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1124 auto tx = vault.set({.owner = owner, .id = keylet.
key});
1130 testCase([&](Env& env,
1132 Account
const& owner,
1135 testcase(
"invalid withdraw amount");
1137 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1140 auto tx = vault.withdraw(
1141 {.depositor = owner,
1148 auto tx = vault.withdraw(
1149 {.depositor = owner, .id = keylet.
key, .amount = asset(0)});
1154 testCase([&](Env& env,
1155 Account
const& issuer,
1156 Account
const& owner,
1161 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1164 auto tx = vault.clawback(
1168 .amount = asset(50)});
1173 auto tx = vault.clawback(
1182 testCase([&](Env& env,
1184 Account
const& owner,
1189 auto [tx1, keylet] = vault.create({.owner = owner, .asset = asset});
1193 tx[sfWithdrawalPolicy] = 0;
1212 tx[sfDomainID] =
"0";
1701 using namespace test::jtx;
1705 bool enableClawback =
true;
1707 int initialXRP = 1000;
1710 auto testCase = [
this](
1713 Account
const& issuer,
1714 Account
const& owner,
1715 Account
const& depositor,
1718 MPTTester& mptt)> test,
1719 CaseArgs args = {}) {
1720 Env env{*
this, testable_amendments() | featureSingleAssetVault};
1721 Account issuer{
"issuer"};
1722 Account owner{
"owner"};
1723 Account depositor{
"depositor"};
1724 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
1728 MPTTester mptt{env, issuer, mptInitNoFund};
1735 mptt.authorize({.account = owner});
1736 mptt.authorize({.account = depositor});
1737 if (args.requireAuth)
1739 mptt.authorize({.account = issuer, .holder = owner});
1740 mptt.authorize({.account = issuer, .holder = depositor});
1743 env(pay(issuer, depositor, asset(1000)));
1746 test(env, issuer, owner, depositor, asset, vault, mptt);
1751 Account
const& issuer,
1752 Account
const& owner,
1753 Account
const& depositor,
1757 testcase(
"MPT nothing to clawback from");
1758 auto tx = vault.clawback(
1761 .holder = depositor,
1762 .amount = asset(10)});
1768 Account
const& issuer,
1769 Account
const& owner,
1770 Account
const& depositor,
1774 testcase(
"MPT global lock blocks create");
1775 mptt.set({.account = issuer, .flags =
tfMPTLock});
1776 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1782 Account
const& issuer,
1783 Account
const& owner,
1784 Account
const& depositor,
1788 testcase(
"MPT global lock blocks deposit");
1789 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1793 mptt.set({.account = issuer, .flags =
tfMPTLock});
1797 {.depositor = depositor,
1799 .amount = asset(100)});
1804 tx = vault.del({.owner = owner, .id = keylet.
key});
1810 Account
const& issuer,
1811 Account
const& owner,
1812 Account
const& depositor,
1816 testcase(
"MPT global lock blocks withdrawal");
1817 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1821 {.depositor = depositor,
1823 .amount = asset(100)});
1829 auto v = env.le(keylet);
1831 MPTID share = (*v)[sfShareMPTID];
1833 BEAST_EXPECT(issuance);
1834 Number outstandingShares = issuance->at(sfOutstandingAmount);
1835 BEAST_EXPECT(outstandingShares == 100);
1837 mptt.set({.account = issuer, .flags =
tfMPTLock});
1840 tx = vault.withdraw(
1841 {.depositor = depositor,
1843 .amount = asset(100)});
1846 tx[sfDestination] = issuer.human();
1850 tx = vault.clawback(
1853 .holder = depositor,
1854 .amount = asset(0)});
1860 BEAST_EXPECT(mptSle ==
nullptr);
1863 tx = vault.del({.owner = owner, .id = keylet.
key});
1869 Account
const& issuer,
1870 Account
const& owner,
1871 Account
const& depositor,
1875 testcase(
"MPT only issuer can clawback");
1877 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
1882 {.depositor = depositor,
1884 .amount = asset(100)});
1889 auto tx = vault.clawback(
1890 {.issuer = owner, .id = keylet.
key, .holder = depositor});
1898 Account
const& issuer,
1899 Account
const& owner,
1900 Account
const& depositor,
1904 testcase(
"MPT depositor without MPToken, auth required");
1907 vault.create({.owner = owner, .asset = asset});
1912 {.depositor = depositor,
1914 .amount = asset(1000)});
1924 auto const mptoken =
1926 auto const sleMPT1 = env.le(mptoken);
1927 BEAST_EXPECT(sleMPT1 ==
nullptr);
1929 tx = vault.withdraw(
1930 {.depositor = depositor,
1932 .amount = asset(100)});
1936 auto const sleMPT2 = env.le(mptoken);
1937 BEAST_EXPECT(sleMPT2 ==
nullptr);
1942 Account charlie{
"charlie"};
1943 env.fund(XRP(1000), charlie);
1946 tx = vault.withdraw(
1947 {.depositor = depositor,
1949 .amount = asset(100)});
1950 tx[sfDestination] = charlie.human();
1954 {.requireAuth =
true});
1959 Account
const& issuer,
1960 Account
const& owner,
1961 Account
const& depositor,
1965 testcase(
"MPT depositor without MPToken, no auth required");
1968 vault.create({.owner = owner, .asset = asset});
1971 auto v = env.le(keylet);
1975 {.depositor = depositor,
1977 .amount = asset(1000)});
1987 auto const mptoken =
1989 auto const sleMPT1 = env.le(mptoken);
1990 BEAST_EXPECT(sleMPT1 ==
nullptr);
1992 tx = vault.withdraw(
1993 {.depositor = depositor,
1995 .amount = asset(100)});
1999 auto const sleMPT2 = env.le(mptoken);
2000 BEAST_EXPECT(sleMPT2 !=
nullptr);
2001 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
2010 auto const mptoken =
2012 auto const sleMPT1 = env.le(mptoken);
2013 BEAST_EXPECT(sleMPT1 ==
nullptr);
2015 tx = vault.withdraw(
2016 {.depositor = depositor,
2018 .amount = asset(100)});
2019 tx[sfDestination] = owner.human();
2023 auto const sleMPT2 = env.le(mptoken);
2024 BEAST_EXPECT(sleMPT2 ==
nullptr);
2027 {.requireAuth =
false});
2030 Env env{*
this, testable_amendments()};
2032 env.current()->fees().accountReserve(0).drops() /
2034 env.current()->fees().increment.drops() /
2041 Account
const& issuer,
2042 Account
const& owner,
2043 Account
const& depositor,
2047 testcase(
"MPT failed reserve to re-create MPToken");
2050 vault.create({.owner = owner, .asset = asset});
2053 auto v = env.le(keylet);
2056 env(pay(depositor, owner, asset(1000)));
2060 {.depositor = owner,
2062 .amount = asset(1000)});
2072 auto const mptoken =
2074 auto const sleMPT = env.le(mptoken);
2075 BEAST_EXPECT(sleMPT ==
nullptr);
2078 tx = vault.withdraw(
2079 {.depositor = owner,
2081 .amount = asset(100)});
2085 env(pay(depositor, owner, XRP(incReserve)));
2093 {.requireAuth =
false,
2094 .initialXRP = acctReserve + incReserve * 4 - 1});
2098 Account
const& issuer,
2099 Account
const& owner,
2100 Account
const& depositor,
2106 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2111 {.depositor = depositor,
2113 .amount = asset(1000)});
2118 auto tx = vault.clawback(
2121 .holder = depositor,
2122 .amount = asset(0)});
2126 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
2131 vault.create({.owner = depositor, .asset = asset});
2136 auto tx = vault.deposit(
2137 {.depositor = depositor,
2139 .amount = asset(10)});
2144 auto tx = vault.withdraw(
2145 {.depositor = depositor,
2147 .amount = asset(10)});
2152 auto tx = vault.clawback(
2155 .holder = depositor,
2156 .amount = asset(0)});
2160 env(vault.del({.owner = owner, .id = keylet.key}));
2165 Account
const& issuer,
2166 Account
const& owner,
2167 Account
const& depositor,
2171 testcase(
"MPT vault owner can receive shares unless unauthorized");
2173 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2178 {.depositor = depositor,
2180 .amount = asset(1000)});
2185 auto const vault = env.le(keylet);
2186 return vault->at(sfShareMPTID);
2192 env(pay(depositor, owner, shares(1)));
2195 tx = vault.withdraw(
2196 {.depositor = owner,
2198 .amount = shares(1)});
2203 env(pay(depositor, owner, shares(1)));
2206 tx = vault.clawback(
2210 .amount = asset(0)});
2215 env(pay(depositor, owner, shares(1)));
2219 env(pay(owner, depositor, shares(1)));
2225 jv[sfAccount] = owner.human();
2226 jv[sfMPTokenIssuanceID] =
to_string(issuanceId);
2228 jv[sfTransactionType] = jss::MPTokenAuthorize;
2234 tx = pay(depositor, owner, shares(1));
2239 tx = vault.clawback(
2242 .holder = depositor,
2243 .amount = asset(0)});
2248 env(vault.del({.owner = owner, .id = keylet.key}));
2256 Account
const& issuer,
2257 Account
const& owner,
2258 Account
const& depositor,
2265 vault.create({.owner = owner, .asset = asset});
2270 {.depositor = depositor,
2272 .amount = asset(1000)});
2277 auto tx = vault.clawback(
2280 .holder = depositor,
2281 .amount = asset(0)});
2285 {.enableClawback =
false});
2289 Account
const& issuer,
2290 Account
const& owner,
2291 Account
const& depositor,
2296 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2300 {.depositor = depositor,
2302 .amount = asset(1000)});
2308 .holder = depositor,
2313 auto tx = vault.withdraw(
2314 {.depositor = depositor,
2316 .amount = asset(100)});
2320 tx[sfDestination] = issuer.human();
2324 tx[sfDestination] = owner.human();
2331 auto tx = vault.deposit(
2332 {.depositor = depositor,
2334 .amount = asset(100)});
2339 tx = vault.clawback(
2342 .holder = depositor,
2343 .amount = asset(800)});
2347 env(vault.del({.owner = owner, .id = keylet.key}));
2352 Account
const& issuer,
2353 Account
const& owner,
2354 Account
const& depositor,
2358 testcase(
"MPT lock of vault pseudo-account");
2359 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2363 auto const vaultAccount =
2364 [&env, keylet = keylet,
this]() ->
AccountID {
2365 auto const vault = env.le(keylet);
2366 BEAST_EXPECT(vault !=
nullptr);
2367 return vault->at(sfAccount);
2371 {.depositor = depositor,
2373 .amount = asset(100)});
2379 jv[jss::Account] = issuer.human();
2380 jv[sfMPTokenIssuanceID] =
2382 jv[jss::Holder] =
toBase58(vaultAccount);
2383 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
2391 {.depositor = depositor,
2393 .amount = asset(100)});
2396 tx = vault.withdraw(
2397 {.depositor = depositor,
2399 .amount = asset(100)});
2403 tx = vault.clawback(
2406 .holder = depositor,
2407 .amount = asset(100)});
2411 tx = vault.del({.owner = owner, .id = keylet.
key});
2418 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2419 Account owner{
"owner"};
2420 Account issuer{
"issuer"};
2421 env.fund(XRP(1000000), owner, issuer);
2425 MPTTester mptt{env, issuer, mptInitNoFund};
2429 mptt.authorize({.account = owner});
2430 mptt.authorize({.account = issuer, .holder = owner});
2432 env(pay(issuer, owner, asset(100)));
2433 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
2437 auto const shares = [&env, keylet = k1,
this]() ->
Asset {
2438 auto const vault = env.le(keylet);
2439 BEAST_EXPECT(vault !=
nullptr);
2440 return MPTIssue(vault->at(sfShareMPTID));
2443 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
2452 using namespace test::jtx;
2456 int initialXRP = 1000;
2464 Account
const& owner,
2465 Account
const& issuer,
2466 Account
const& charlie,
2471 CaseArgs args = {}) {
2472 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2473 Account
const owner{
"owner"};
2474 Account
const issuer{
"issuer"};
2475 Account
const charlie{
"charlie"};
2477 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2482 env.trust(asset(1000), owner);
2483 env.trust(asset(1000), charlie);
2484 env(pay(issuer, owner, asset(200)));
2485 env(rate(issuer, args.transferRate));
2488 auto const vaultAccount =
2490 return Account(
"vault", env.le(keylet)->at(sfAccount));
2493 return env.le(keylet)->at(sfShareMPTID);
2509 Account
const& owner,
2510 Account
const& issuer,
2516 testcase(
"IOU cannot use different asset");
2519 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2525 auto tx = [&, account = vaultAccount(keylet)]() {
2527 jv[jss::Account] = issuer.human();
2529 auto& ja = jv[jss::LimitAmount] =
2531 ja[jss::issuer] =
toBase58(account);
2533 jv[jss::TransactionType] = jss::TrustSet;
2542 auto tx = vault.deposit(
2543 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2549 auto tx = vault.withdraw(
2550 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2555 env(vault.del({.owner = owner, .id = keylet.key}));
2561 Account
const& owner,
2562 Account
const& issuer,
2563 Account
const& charlie,
2568 testcase(
"IOU frozen trust line to vault account");
2570 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2575 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2578 Asset const share =
Asset(issuanceId(keylet));
2581 auto trustSet = [&, account = vaultAccount(keylet)]() {
2583 jv[jss::Account] = issuer.human();
2585 auto& ja = jv[jss::LimitAmount] =
2587 ja[jss::issuer] =
toBase58(account);
2589 jv[jss::TransactionType] = jss::TrustSet;
2601 auto tx = vault.deposit(
2602 {.depositor = owner,
2604 .amount = asset(80)});
2609 auto tx = vault.withdraw(
2610 {.depositor = owner,
2612 .amount = asset(100)});
2616 tx[sfDestination] = charlie.human();
2623 auto tx = vault.clawback(
2627 .amount = asset(50)});
2638 {.depositor = owner,
2640 .amount = share(50'000'000)}));
2642 env(vault.del({.owner = owner, .id = keylet.key}));
2649 Account
const& owner,
2650 Account
const& issuer,
2651 Account
const& charlie,
2656 testcase(
"IOU transfer fees not applied");
2659 vault.create({.owner = owner, .asset = asset});
2664 {.depositor = owner,
2666 .amount = asset(100)}));
2670 Asset const share =
Asset(issuanceId(keylet));
2673 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2675 env.balance(vaultAccount(keylet), issue) == asset(100));
2678 auto tx = vault.clawback(
2682 .amount = asset(50)});
2688 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2690 env.balance(vaultAccount(keylet), issue) == asset(50));
2693 {.depositor = owner,
2695 .amount = share(20'000'000)}));
2698 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2700 env.balance(vaultAccount(keylet), issue) == asset(30));
2703 auto tx = vault.withdraw(
2704 {.depositor = owner,
2706 .amount = share(30'000'000)});
2707 tx[sfDestination] = charlie.human();
2712 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2713 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2715 env.balance(vaultAccount(keylet), issue) == asset(0));
2717 env(vault.del({.owner = owner, .id = keylet.key}));
2720 CaseArgs{.transferRate = 1.25});
2724 Account
const& owner,
2725 Account
const& issuer,
2726 Account
const& charlie,
2731 testcase(
"IOU frozen trust line to depositor");
2733 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2738 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2743 auto tx = vault.withdraw(
2744 {.depositor = owner,
2746 .amount = asset(10)});
2747 tx[sfDestination] = charlie.human();
2750 env(withdrawToCharlie);
2757 auto const withdraw = vault.withdraw(
2758 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2767 auto tx = vault.deposit(
2768 {.depositor = owner,
2770 .amount = asset(10)});
2776 auto tx = vault.clawback(
2780 .amount = asset(0)});
2785 env(vault.del({.owner = owner, .id = keylet.key}));
2791 Account
const& owner,
2792 Account
const& issuer,
2793 Account
const& charlie,
2798 testcase(
"IOU no trust line to 3rd party");
2800 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2805 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2808 Account
const erin{
"erin"};
2809 env.fund(XRP(1000), erin);
2814 auto tx = vault.withdraw(
2815 {.depositor = owner,
2817 .amount = asset(10)});
2818 tx[sfDestination] = erin.human();
2826 Account
const& owner,
2827 Account
const& issuer,
2828 Account
const& charlie,
2833 testcase(
"IOU no trust line to depositor");
2835 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2840 env.trust(asset(0), owner);
2844 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2849 BEAST_EXPECT(trustline ==
nullptr);
2853 auto tx = vault.withdraw(
2854 {.depositor = owner,
2856 .amount = asset(10)});
2863 Env env{*
this, testable_amendments()};
2865 env.current()->fees().accountReserve(0).drops() /
2867 env.current()->fees().increment.drops() /
2874 Account
const& owner,
2875 Account
const& issuer,
2876 Account
const& charlie,
2881 testcase(
"IOU no trust line to depositor no reserve");
2883 vault.create({.owner = owner, .asset = asset});
2889 env.trust(asset(0), owner);
2893 {.depositor = owner,
2895 .amount = asset(200)}));
2900 BEAST_EXPECT(trustline ==
nullptr);
2903 tx = vault.withdraw(
2904 {.depositor = owner,
2906 .amount = asset(10)});
2910 env(pay(charlie, owner, XRP(incReserve)));
2917 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
2922 Account
const& owner,
2923 Account
const& issuer,
2924 Account
const& charlie,
2929 testcase(
"IOU no reserve for share MPToken");
2931 vault.create({.owner = owner, .asset = asset});
2935 env(pay(owner, charlie, asset(100)));
2939 env(ticket::create(charlie, 2));
2944 {.depositor = charlie,
2946 .amount = asset(100)});
2950 env(pay(issuer, charlie, XRP(incReserve)));
2957 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
2961 Account
const& owner,
2962 Account
const& issuer,
2963 Account
const& charlie,
2968 testcase(
"IOU frozen trust line to 3rd party");
2970 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2975 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2980 auto tx = vault.withdraw(
2981 {.depositor = owner,
2983 .amount = asset(10)});
2984 tx[sfDestination] = charlie.human();
2987 env(withdrawToCharlie);
2990 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
2994 auto const withdraw = vault.withdraw(
2995 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
3007 .amount = asset(0)}));
3010 env(vault.del({.owner = owner, .id = keylet.key}));
3016 Account
const& owner,
3017 Account
const& issuer,
3018 Account
const& charlie,
3025 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3030 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3038 auto tx = vault.withdraw(
3039 {.depositor = owner,
3041 .amount = asset(10)});
3045 tx[sfDestination] = charlie.human();
3051 {.depositor = owner,
3053 .amount = asset(10)});
3063 .amount = asset(0)}));
3066 env(vault.del({.owner = owner, .id = keylet.key}));
3074 using namespace test::jtx;
3078 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3079 Account issuer{
"issuer"};
3080 Account owner{
"owner"};
3081 Account depositor{
"depositor"};
3082 Account charlie{
"charlie"};
3083 Account pdOwner{
"pdOwner"};
3084 Account credIssuer1{
"credIssuer1"};
3085 Account credIssuer2{
"credIssuer2"};
3103 env.trust(asset(1000), owner);
3104 env(pay(issuer, owner, asset(500)));
3105 env.trust(asset(1000), depositor);
3106 env(pay(issuer, depositor, asset(500)));
3107 env.trust(asset(1000), charlie);
3108 env(pay(issuer, charlie, asset(5)));
3111 auto [tx, keylet] = vault.create(
3115 BEAST_EXPECT(env.le(keylet));
3118 testcase(
"private vault owner can deposit");
3119 auto tx = vault.deposit(
3120 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
3125 testcase(
"private vault depositor not authorized yet");
3126 auto tx = vault.deposit(
3127 {.depositor = depositor,
3129 .amount = asset(50)});
3134 testcase(
"private vault cannot set non-existing domain");
3135 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3141 testcase(
"private vault set domainId");
3144 pdomain::Credentials
const credentials1{
3145 {.issuer = credIssuer1, .credType = credType}};
3147 env(pdomain::setTx(pdOwner, credentials1));
3148 auto const domainId1 = [&]() {
3150 return pdomain::getNewDomain(env.meta());
3153 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3164 pdomain::Credentials
const credentials{
3165 {.issuer = credIssuer1, .credType = credType},
3166 {.issuer = credIssuer2, .credType = credType}};
3168 env(pdomain::setTx(pdOwner, credentials));
3169 auto const domainId = [&]() {
3171 return pdomain::getNewDomain(env.meta());
3174 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3180 tx = vault.set({.owner = owner, .id = keylet.
key});
3188 testcase(
"private vault depositor still not authorized");
3189 auto tx = vault.deposit(
3190 {.depositor = depositor,
3192 .amount = asset(50)});
3197 auto const credKeylet =
3198 credentials::keylet(depositor, credIssuer1, credType);
3200 testcase(
"private vault depositor now authorized");
3201 env(credentials::create(depositor, credIssuer1, credType));
3202 env(credentials::accept(depositor, credIssuer1, credType));
3203 env(credentials::create(charlie, credIssuer1, credType));
3206 auto credSle = env.le(credKeylet);
3207 BEAST_EXPECT(credSle !=
nullptr);
3209 auto tx = vault.deposit(
3210 {.depositor = depositor,
3212 .amount = asset(50)});
3217 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
3223 testcase(
"private vault depositor lost authorization");
3224 env(credentials::deleteCred(
3225 credIssuer1, depositor, credIssuer1, credType));
3226 env(credentials::deleteCred(
3227 credIssuer1, charlie, credIssuer1, credType));
3229 auto credSle = env.le(credKeylet);
3230 BEAST_EXPECT(credSle ==
nullptr);
3232 auto tx = vault.deposit(
3233 {.depositor = depositor,
3235 .amount = asset(50)});
3240 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
3241 auto const vault = env.le(keylet);
3242 BEAST_EXPECT(vault !=
nullptr);
3243 return MPTIssue(vault->at(sfShareMPTID));
3247 testcase(
"private vault expired authorization");
3248 uint32_t
const closeTime = env.current()
3250 .parentCloseTime.time_since_epoch()
3254 credentials::create(depositor, credIssuer2, credType);
3255 tx0[sfExpiration] = closeTime + 20;
3257 tx0 = credentials::create(charlie, credIssuer2, credType);
3258 tx0[sfExpiration] = closeTime + 20;
3262 env(credentials::accept(depositor, credIssuer2, credType));
3263 env(credentials::accept(charlie, credIssuer2, credType));
3268 auto tx1 = vault.deposit(
3269 {.depositor = depositor,
3271 .amount = asset(50)});
3277 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3286 auto const credsKeylet =
3287 credentials::keylet(depositor, credIssuer2, credType);
3288 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3290 auto tx2 = vault.deposit(
3291 {.depositor = depositor,
3293 .amount = asset(1)});
3297 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3301 auto const credsKeylet =
3302 credentials::keylet(charlie, credIssuer2, credType);
3303 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3306 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3308 auto tx3 = vault.deposit(
3309 {.depositor = charlie,
3311 .amount = asset(2)});
3315 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3316 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3321 testcase(
"private vault reset domainId");
3322 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3323 tx[sfDomainID] =
"0";
3328 {.depositor = depositor,
3330 .amount = asset(50)});
3334 tx = vault.withdraw(
3335 {.depositor = depositor,
3337 .amount = asset(50)});
3341 tx = vault.clawback(
3344 .holder = depositor,
3345 .amount = asset(0)});
3348 tx = vault.clawback(
3352 .amount = asset(0)});
3501 using namespace test::jtx;
3505 Account
const& owner;
3506 Account
const& issuer;
3507 Account
const& depositor;
3508 Account
const& vaultAccount;
3518 auto testCase = [&,
this](
3521 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3522 Account
const owner{
"owner"};
3523 Account
const issuer{
"issuer"};
3524 Account
const depositor{
"depositor"};
3526 env.fund(XRP(1000), issuer, owner, depositor);
3531 env.trust(asset(1000), owner);
3532 env.trust(asset(1000), depositor);
3533 env(pay(issuer, owner, asset(200)));
3534 env(pay(issuer, depositor, asset(200)));
3537 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3538 tx[sfScale] = scale;
3541 auto const [vaultAccount, issuanceId] =
3543 auto const vault = env.le(keylet);
3545 Account(
"vault", vault->at(sfAccount)),
3546 vault->at(sfShareMPTID)};
3549 env.memoize(vaultAccount);
3553 return env.app().openLedger().modify(
3557 if (!BEAST_EXPECT(vault !=
nullptr))
3559 auto shares = sb.
peek(
3561 if (!BEAST_EXPECT(shares !=
nullptr))
3563 if (fn(*vault, *shares))
3578 .depositor = depositor,
3579 .vaultAccount = vaultAccount,
3589 testCase(18, [&,
this](Env& env, Data d) {
3590 testcase(
"Scale deposit overflow on first deposit");
3591 auto tx = d.vault.deposit(
3592 {.depositor = d.depositor,
3594 .amount = d.asset(10)});
3599 testCase(18, [&,
this](Env& env, Data d) {
3600 testcase(
"Scale deposit overflow on second deposit");
3603 auto tx = d.vault.deposit(
3604 {.depositor = d.depositor,
3606 .amount = d.asset(5)});
3612 auto tx = d.vault.deposit(
3613 {.depositor = d.depositor,
3615 .amount = d.asset(10)});
3621 testCase(18, [&,
this](Env& env, Data d) {
3622 testcase(
"Scale deposit overflow on total shares");
3625 auto tx = d.vault.deposit(
3626 {.depositor = d.depositor,
3628 .amount = d.asset(5)});
3634 auto tx = d.vault.deposit(
3635 {.depositor = d.depositor,
3637 .amount = d.asset(5)});
3643 testCase(1, [&,
this](Env& env, Data d) {
3646 auto const start = env.balance(d.depositor, d.assets).number();
3647 auto tx = d.vault.deposit(
3648 {.depositor = d.depositor,
3650 .amount = d.asset(1)});
3653 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3655 env.balance(d.depositor, d.assets) ==
3659 testCase(1, [&,
this](Env& env, Data d) {
3660 testcase(
"Scale deposit insignificant amount");
3662 auto tx = d.vault.deposit(
3663 {.depositor = d.depositor,
3669 testCase(1, [&,
this](Env& env, Data d) {
3670 testcase(
"Scale deposit exact, using full precision");
3672 auto const start = env.balance(d.depositor, d.assets).number();
3673 auto tx = d.vault.deposit(
3674 {.depositor = d.depositor,
3679 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3681 env.balance(d.depositor, d.assets) ==
3685 testCase(1, [&,
this](Env& env, Data d) {
3686 testcase(
"Scale deposit exact, truncating from .5");
3688 auto const start = env.balance(d.depositor, d.assets).number();
3692 auto tx = d.vault.deposit(
3693 {.depositor = d.depositor,
3698 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3700 env.balance(d.depositor, d.assets) ==
3705 auto tx = d.vault.deposit(
3706 {.depositor = d.depositor,
3711 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3713 env.balance(d.depositor, d.assets) ==
3718 auto tx = d.vault.deposit(
3719 {.depositor = d.depositor,
3724 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3726 env.balance(d.depositor, d.assets) ==
3731 testCase(1, [&,
this](Env& env, Data d) {
3732 testcase(
"Scale deposit exact, truncating from .01");
3734 auto const start = env.balance(d.depositor, d.assets).number();
3736 auto tx = d.vault.deposit(
3737 {.depositor = d.depositor,
3742 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3744 env.balance(d.depositor, d.assets) ==
3749 auto tx = d.vault.deposit(
3750 {.depositor = d.depositor,
3755 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3757 env.balance(d.depositor, d.assets) ==
3762 testCase(1, [&,
this](Env& env, Data d) {
3763 testcase(
"Scale deposit exact, truncating from .99");
3765 auto const start = env.balance(d.depositor, d.assets).number();
3767 auto tx = d.vault.deposit(
3768 {.depositor = d.depositor,
3773 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3775 env.balance(d.depositor, d.assets) ==
3780 auto tx = d.vault.deposit(
3781 {.depositor = d.depositor,
3786 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3788 env.balance(d.depositor, d.assets) ==
3793 testCase(1, [&,
this](Env& env, Data d) {
3795 auto const start = env.balance(d.depositor, d.assets).number();
3796 auto tx = d.vault.deposit(
3797 {.depositor = d.depositor,
3802 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3804 env.balance(d.depositor, d.assets) ==
3807 env.balance(d.vaultAccount, d.assets) ==
3810 env.balance(d.vaultAccount, d.shares) ==
3819 auto const start = env.balance(d.depositor, d.assets).number();
3820 auto tx = d.vault.withdraw(
3821 {.depositor = d.depositor,
3827 env.balance(d.depositor, d.shares) == d.share(900));
3829 env.balance(d.depositor, d.assets) ==
3832 env.balance(d.vaultAccount, d.assets) ==
3835 env.balance(d.vaultAccount, d.shares) ==
3840 testcase(
"Scale redeem with rounding");
3845 auto const start = env.balance(d.depositor, d.assets).number();
3846 d.peek([](
SLE& vault,
auto&) ->
bool {
3847 vault[sfAssetsAvailable] =
Number(1);
3855 auto tx = d.vault.withdraw(
3856 {.depositor = d.depositor,
3862 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3864 env.balance(d.depositor, d.assets) ==
3867 env.balance(d.vaultAccount, d.assets) ==
3870 env.balance(d.vaultAccount, d.shares) ==
3880 auto const start = env.balance(d.depositor, d.assets).number();
3882 tx = d.vault.withdraw(
3883 {.depositor = d.depositor,
3889 env.balance(d.depositor, d.shares) == d.share(875 - 21));
3891 env.balance(d.depositor, d.assets) ==
3894 env.balance(d.vaultAccount, d.assets) ==
3897 env.balance(d.vaultAccount, d.shares) ==
3903 auto const rest = env.balance(d.depositor, d.shares).number();
3905 tx = d.vault.withdraw(
3906 {.depositor = d.depositor,
3908 .amount =
STAmount(d.share, rest)});
3911 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3913 env.balance(d.vaultAccount, d.assets).number() == 0);
3915 env.balance(d.vaultAccount, d.shares).number() == 0);
3919 testCase(18, [&,
this](Env& env, Data d) {
3920 testcase(
"Scale withdraw overflow");
3923 auto tx = d.vault.deposit(
3924 {.depositor = d.depositor,
3926 .amount = d.asset(5)});
3932 auto tx = d.vault.withdraw(
3933 {.depositor = d.depositor,
3941 testCase(1, [&,
this](Env& env, Data d) {
3943 auto const start = env.balance(d.depositor, d.assets).number();
3944 auto tx = d.vault.deposit(
3945 {.depositor = d.depositor,
3950 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3952 env.balance(d.depositor, d.assets) ==
3955 env.balance(d.vaultAccount, d.assets) ==
3958 env.balance(d.vaultAccount, d.shares) ==
3970 auto const start = env.balance(d.depositor, d.assets).number();
3971 auto tx = d.vault.withdraw(
3972 {.depositor = d.depositor,
3978 env.balance(d.depositor, d.shares) == d.share(900));
3980 env.balance(d.depositor, d.assets) ==
3983 env.balance(d.vaultAccount, d.assets) ==
3986 env.balance(d.vaultAccount, d.shares) ==
3991 testcase(
"Scale withdraw insignificant amount");
3992 auto tx = d.vault.withdraw(
3993 {.depositor = d.depositor,
4000 testcase(
"Scale withdraw with rounding assets");
4008 auto const start = env.balance(d.depositor, d.assets).number();
4009 d.peek([](
SLE& vault,
auto&) ->
bool {
4010 vault[sfAssetsAvailable] =
Number(1);
4018 auto tx = d.vault.withdraw(
4019 {.depositor = d.depositor,
4025 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4027 env.balance(d.depositor, d.assets) ==
4030 env.balance(d.vaultAccount, d.assets) ==
4033 env.balance(d.vaultAccount, d.shares) ==
4038 testcase(
"Scale withdraw with rounding shares up");
4046 auto const start = env.balance(d.depositor, d.assets).number();
4047 auto tx = d.vault.withdraw(
4048 {.depositor = d.depositor,
4054 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4056 env.balance(d.depositor, d.assets) ==
4059 env.balance(d.vaultAccount, d.assets) ==
4062 env.balance(d.vaultAccount, d.shares) ==
4067 testcase(
"Scale withdraw with rounding shares down");
4075 auto const start = env.balance(d.depositor, d.assets).number();
4076 auto tx = d.vault.withdraw(
4077 {.depositor = d.depositor,
4083 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4085 env.balance(d.depositor, d.assets) ==
4088 env.balance(d.vaultAccount, d.assets) ==
4091 env.balance(d.vaultAccount, d.shares) ==
4096 testcase(
"Scale withdraw tiny amount");
4098 auto const start = env.balance(d.depositor, d.assets).number();
4099 auto tx = d.vault.withdraw(
4100 {.depositor = d.depositor,
4106 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4108 env.balance(d.depositor, d.assets) ==
4111 env.balance(d.vaultAccount, d.assets) ==
4114 env.balance(d.vaultAccount, d.shares) ==
4121 env.balance(d.vaultAccount, d.assets).number();
4123 tx = d.vault.withdraw(
4124 {.depositor = d.depositor,
4126 .amount =
STAmount(d.asset, rest)});
4129 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4131 env.balance(d.vaultAccount, d.assets).number() == 0);
4133 env.balance(d.vaultAccount, d.shares).number() == 0);
4137 testCase(18, [&,
this](Env& env, Data d) {
4138 testcase(
"Scale clawback overflow");
4141 auto tx = d.vault.deposit(
4142 {.depositor = d.depositor,
4144 .amount = d.asset(5)});
4150 auto tx = d.vault.clawback(
4151 {.issuer = d.issuer,
4153 .holder = d.depositor,
4160 testCase(1, [&,
this](Env& env, Data d) {
4162 auto const start = env.balance(d.depositor, d.assets).number();
4163 auto tx = d.vault.deposit(
4164 {.depositor = d.depositor,
4169 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4171 env.balance(d.depositor, d.assets) ==
4174 env.balance(d.vaultAccount, d.assets) ==
4177 env.balance(d.vaultAccount, d.shares) ==
4188 auto const start = env.balance(d.depositor, d.assets).number();
4189 auto tx = d.vault.clawback(
4190 {.issuer = d.issuer,
4192 .holder = d.depositor,
4197 env.balance(d.depositor, d.shares) == d.share(900));
4199 env.balance(d.depositor, d.assets) ==
4202 env.balance(d.vaultAccount, d.assets) ==
4205 env.balance(d.vaultAccount, d.shares) ==
4210 testcase(
"Scale clawback insignificant amount");
4211 auto tx = d.vault.clawback(
4212 {.issuer = d.issuer,
4214 .holder = d.depositor,
4220 testcase(
"Scale clawback with rounding assets");
4228 auto const start = env.balance(d.depositor, d.assets).number();
4229 auto tx = d.vault.clawback(
4230 {.issuer = d.issuer,
4232 .holder = d.depositor,
4237 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4239 env.balance(d.depositor, d.assets) ==
4242 env.balance(d.vaultAccount, d.assets) ==
4245 env.balance(d.vaultAccount, d.shares) ==
4250 testcase(
"Scale clawback with rounding shares up");
4258 auto const start = env.balance(d.depositor, d.assets).number();
4259 auto tx = d.vault.clawback(
4260 {.issuer = d.issuer,
4262 .holder = d.depositor,
4267 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4269 env.balance(d.depositor, d.assets) ==
4272 env.balance(d.vaultAccount, d.assets) ==
4275 env.balance(d.vaultAccount, d.shares) ==
4280 testcase(
"Scale clawback with rounding shares down");
4288 auto const start = env.balance(d.depositor, d.assets).number();
4289 auto tx = d.vault.clawback(
4290 {.issuer = d.issuer,
4292 .holder = d.depositor,
4297 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4299 env.balance(d.depositor, d.assets) ==
4302 env.balance(d.vaultAccount, d.assets) ==
4305 env.balance(d.vaultAccount, d.shares) ==
4310 testcase(
"Scale clawback tiny amount");
4312 auto const start = env.balance(d.depositor, d.assets).number();
4313 auto tx = d.vault.clawback(
4314 {.issuer = d.issuer,
4316 .holder = d.depositor,
4321 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4323 env.balance(d.depositor, d.assets) ==
4326 env.balance(d.vaultAccount, d.assets) ==
4329 env.balance(d.vaultAccount, d.shares) ==
4336 env.balance(d.vaultAccount, d.assets).number();
4337 d.peek([](
SLE& vault,
auto&) ->
bool {
4338 vault[sfAssetsAvailable] =
Number(5);
4346 tx = d.vault.clawback(
4347 {.issuer = d.issuer,
4349 .holder = d.depositor,
4350 .amount =
STAmount(d.asset, rest)});
4353 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4355 env.balance(d.vaultAccount, d.assets).number() == 0);
4357 env.balance(d.vaultAccount, d.shares).number() == 0);
4365 using namespace test::jtx;
4368 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4369 Account
const owner{
"owner"};
4370 Account
const issuer{
"issuer"};
4372 env.fund(XRP(1000), issuer, owner);
4376 env.trust(asset(1000), owner);
4377 env(pay(issuer, owner, asset(200)));
4380 auto const sequence = env.seq(owner);
4381 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4387 auto tx1 = vault.deposit(
4388 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4391 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4392 tx2[sfAssetsMaximum] = asset(1000).number();
4397 auto const sleVault = [&env, keylet = keylet,
this]() {
4398 auto const vault = env.le(keylet);
4399 BEAST_EXPECT(vault !=
nullptr);
4403 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4406 BEAST_EXPECT(vault.isObject());
4408 constexpr auto checkString =
4410 return node.isMember(field.fieldName) &&
4411 node[field.fieldName].isString() &&
4412 node[field.fieldName] == v;
4414 constexpr auto checkObject =
4416 return node.isMember(field.fieldName) &&
4417 node[field.fieldName].isObject() &&
4418 node[field.fieldName] == v;
4420 constexpr auto checkInt =
4421 [](
auto& node,
SField const& field,
int v) ->
bool {
4422 return node.isMember(field.fieldName) &&
4423 ((node[field.fieldName].isInt() &&
4424 node[field.fieldName] ==
Json::Int(v)) ||
4425 (node[field.fieldName].isUInt() &&
4429 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4430 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4431 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4435 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4437 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4438 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4439 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4440 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4441 BEAST_EXPECT(checkString(vault, sfLossUnrealized,
"0"));
4443 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4444 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4445 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4446 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4447 BEAST_EXPECT(checkInt(
4450 if (issuance.isObject())
4453 issuance[
"LedgerEntryType"].asString() ==
4456 issuance[jss::mpt_issuance_id].asString() == strShareID);
4457 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4458 BEAST_EXPECT(checkInt(
4463 checkString(issuance, sfOutstandingAmount,
"50000000"));
4468 testcase(
"RPC ledger_entry selected by key");
4470 jvParams[jss::ledger_index] = jss::validated;
4471 jvParams[jss::vault] =
strHex(keylet.
key);
4472 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4474 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4475 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4476 check(jvVault[jss::result][jss::node]);
4480 testcase(
"RPC ledger_entry selected by owner and seq");
4482 jvParams[jss::ledger_index] = jss::validated;
4483 jvParams[jss::vault][jss::owner] = owner.human();
4484 jvParams[jss::vault][jss::seq] = sequence;
4485 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4487 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4488 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4489 check(jvVault[jss::result][jss::node]);
4493 testcase(
"RPC ledger_entry cannot find vault by key");
4495 jvParams[jss::ledger_index] = jss::validated;
4497 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4499 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4503 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4505 jvParams[jss::ledger_index] = jss::validated;
4506 jvParams[jss::vault][jss::owner] = issuer.human();
4507 jvParams[jss::vault][jss::seq] = 1'000'000;
4508 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4510 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4514 testcase(
"RPC ledger_entry malformed key");
4516 jvParams[jss::ledger_index] = jss::validated;
4517 jvParams[jss::vault] = 42;
4518 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4520 jvVault[jss::result][jss::error].asString() ==
4521 "malformedRequest");
4525 testcase(
"RPC ledger_entry malformed owner");
4527 jvParams[jss::ledger_index] = jss::validated;
4528 jvParams[jss::vault][jss::owner] = 42;
4529 jvParams[jss::vault][jss::seq] = sequence;
4530 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4532 jvVault[jss::result][jss::error].asString() ==
4537 testcase(
"RPC ledger_entry malformed seq");
4539 jvParams[jss::ledger_index] = jss::validated;
4540 jvParams[jss::vault][jss::owner] = issuer.human();
4541 jvParams[jss::vault][jss::seq] =
"foo";
4542 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4544 jvVault[jss::result][jss::error].asString() ==
4545 "malformedRequest");
4549 testcase(
"RPC ledger_entry negative seq");
4551 jvParams[jss::ledger_index] = jss::validated;
4552 jvParams[jss::vault][jss::owner] = issuer.human();
4553 jvParams[jss::vault][jss::seq] = -1;
4554 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4556 jvVault[jss::result][jss::error].asString() ==
4557 "malformedRequest");
4561 testcase(
"RPC ledger_entry oversized seq");
4563 jvParams[jss::ledger_index] = jss::validated;
4564 jvParams[jss::vault][jss::owner] = issuer.human();
4565 jvParams[jss::vault][jss::seq] = 1e20;
4566 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4568 jvVault[jss::result][jss::error].asString() ==
4569 "malformedRequest");
4573 testcase(
"RPC ledger_entry bool seq");
4575 jvParams[jss::ledger_index] = jss::validated;
4576 jvParams[jss::vault][jss::owner] = issuer.human();
4577 jvParams[jss::vault][jss::seq] =
true;
4578 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4580 jvVault[jss::result][jss::error].asString() ==
4581 "malformedRequest");
4588 jvParams[jss::account] = owner.human();
4589 jvParams[jss::type] = jss::vault;
4591 "json",
"account_objects",
to_string(jvParams))[jss::result];
4593 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4594 check(jv[jss::account_objects][0u]);
4601 jvParams[jss::ledger_index] = jss::validated;
4602 jvParams[jss::binary] =
false;
4603 jvParams[jss::type] = jss::vault;
4605 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4606 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4607 check(jv[jss::result][jss::state][0u]);
4611 testcase(
"RPC vault_info command line");
4613 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4615 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4616 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4618 jv[jss::result][jss::vault],
4619 jv[jss::result][jss::vault][jss::shares]);
4625 jvParams[jss::ledger_index] = jss::validated;
4626 jvParams[jss::vault_id] =
strHex(keylet.
key);
4627 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4629 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4630 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4632 jv[jss::result][jss::vault],
4633 jv[jss::result][jss::vault][jss::shares]);
4637 testcase(
"RPC vault_info invalid vault_id");
4639 jvParams[jss::ledger_index] = jss::validated;
4640 jvParams[jss::vault_id] =
"foobar";
4641 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4643 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4647 testcase(
"RPC vault_info json invalid index");
4649 jvParams[jss::ledger_index] = jss::validated;
4650 jvParams[jss::vault_id] = 0;
4651 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4653 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4657 testcase(
"RPC vault_info json by owner and sequence");
4659 jvParams[jss::ledger_index] = jss::validated;
4660 jvParams[jss::owner] = owner.human();
4661 jvParams[jss::seq] = sequence;
4662 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4664 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4665 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4667 jv[jss::result][jss::vault],
4668 jv[jss::result][jss::vault][jss::shares]);
4672 testcase(
"RPC vault_info json malformed sequence");
4674 jvParams[jss::ledger_index] = jss::validated;
4675 jvParams[jss::owner] = owner.human();
4676 jvParams[jss::seq] =
"foobar";
4677 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4679 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4683 testcase(
"RPC vault_info json invalid sequence");
4685 jvParams[jss::ledger_index] = jss::validated;
4686 jvParams[jss::owner] = owner.human();
4687 jvParams[jss::seq] = 0;
4688 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4690 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4694 testcase(
"RPC vault_info json negative sequence");
4696 jvParams[jss::ledger_index] = jss::validated;
4697 jvParams[jss::owner] = owner.human();
4698 jvParams[jss::seq] = -1;
4699 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4701 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4705 testcase(
"RPC vault_info json oversized sequence");
4707 jvParams[jss::ledger_index] = jss::validated;
4708 jvParams[jss::owner] = owner.human();
4709 jvParams[jss::seq] = 1e20;
4710 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4712 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4716 testcase(
"RPC vault_info json bool sequence");
4718 jvParams[jss::ledger_index] = jss::validated;
4719 jvParams[jss::owner] = owner.human();
4720 jvParams[jss::seq] =
true;
4721 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4723 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4727 testcase(
"RPC vault_info json malformed owner");
4729 jvParams[jss::ledger_index] = jss::validated;
4730 jvParams[jss::owner] =
"foobar";
4731 jvParams[jss::seq] = sequence;
4732 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4734 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4738 testcase(
"RPC vault_info json invalid combination only owner");
4740 jvParams[jss::ledger_index] = jss::validated;
4741 jvParams[jss::owner] = owner.human();
4742 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4744 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4748 testcase(
"RPC vault_info json invalid combination only seq");
4750 jvParams[jss::ledger_index] = jss::validated;
4751 jvParams[jss::seq] = sequence;
4752 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4754 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4758 testcase(
"RPC vault_info json invalid combination seq vault_id");
4760 jvParams[jss::ledger_index] = jss::validated;
4761 jvParams[jss::vault_id] =
strHex(keylet.
key);
4762 jvParams[jss::seq] = sequence;
4763 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4765 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4769 testcase(
"RPC vault_info json invalid combination owner vault_id");
4771 jvParams[jss::ledger_index] = jss::validated;
4772 jvParams[jss::vault_id] =
strHex(keylet.
key);
4773 jvParams[jss::owner] = owner.human();
4774 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4776 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4781 "RPC vault_info json invalid combination owner seq "
4784 jvParams[jss::ledger_index] = jss::validated;
4785 jvParams[jss::vault_id] =
strHex(keylet.
key);
4786 jvParams[jss::seq] = sequence;
4787 jvParams[jss::owner] = owner.human();
4788 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4790 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4794 testcase(
"RPC vault_info json no input");
4796 jvParams[jss::ledger_index] = jss::validated;
4797 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4799 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4803 testcase(
"RPC vault_info command line invalid index");
4804 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4805 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4809 testcase(
"RPC vault_info command line invalid index");
4810 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4812 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4816 testcase(
"RPC vault_info command line invalid index");
4820 jv[jss::result][jss::error].asString() ==
"entryNotFound");
4824 testcase(
"RPC vault_info command line invalid ledger");
4827 jv[jss::result][jss::error].asString() ==
"lgrNotFound");
4834 using namespace test::jtx;
4836 Env env(*
this, testable_amendments());
4837 Account alice{
"alice"};
4839 Account carol{
"carol"};
4846 auto const xrpBalance =
4850 if (BEAST_EXPECT(sle !=
nullptr))
4851 return sle->getFieldAmount(sfBalance).xrp().drops();
4855 auto testCase = [&,
this](
auto test, CaseArgs args = {}) {
4856 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4861 env.fund(XRP(10000), alice);
4862 env.fund(XRP(20000), bob);
4863 env.fund(XRP(30000), carol);
4877 test(env, vault, args.asset);
4880 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4881 testcase(
"delegated vault creation");
4882 auto startBalance = xrpBalance(env, carol);
4883 if (!BEAST_EXPECT(startBalance.has_value()))
4886 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4887 env(tx, delegate::as(alice));
4889 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
4892 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4893 testcase(
"delegated deposit and withdrawal");
4894 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4898 auto const amount = 1513;
4899 auto const baseFee = env.current()->fees().base;
4901 auto startBalance = xrpBalance(env, carol);
4902 if (!BEAST_EXPECT(startBalance.has_value()))
4906 {.depositor = carol,
4908 .amount = asset(amount)});
4909 env(tx, delegate::as(alice));
4911 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
4913 tx = vault.withdraw(
4914 {.depositor = carol,
4916 .amount = asset(amount - 1)});
4917 env(tx, delegate::as(alice));
4919 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
4921 tx = vault.withdraw(
4922 {.depositor = carol, .id = keylet.
key, .amount = asset(1)});
4925 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
4928 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4929 testcase(
"delegated withdrawal same as base fee and deletion");
4930 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4934 auto const amount = 25537;
4935 auto const baseFee = env.current()->fees().base;
4937 auto startBalance = xrpBalance(env, carol);
4938 if (!BEAST_EXPECT(startBalance.has_value()))
4942 {.depositor = carol,
4944 .amount = asset(amount)});
4948 xrpBalance(env, carol) == *startBalance - amount - baseFee);
4950 tx = vault.withdraw(
4951 {.depositor = carol,
4953 .amount = asset(baseFee)});
4954 env(tx, delegate::as(alice));
4956 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
4958 tx = vault.withdraw(
4959 {.depositor = carol,
4961 .amount = asset(amount - baseFee)});
4962 env(tx, delegate::as(alice));
4964 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
4966 tx = vault.del({.owner = carol, .id = keylet.
key});
4967 env(tx, delegate::as(alice));