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;
2465 Account
const& owner,
2466 Account
const& issuer,
2467 Account
const& charlie,
2472 CaseArgs args = {}) {
2473 Env env{*
this, testable_amendments() | featureSingleAssetVault};
2474 Account
const owner{
"owner"};
2475 Account
const issuer{
"issuer"};
2476 Account
const charlie{
"charlie"};
2478 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
2483 env.trust(asset(1000), owner);
2484 env.trust(asset(1000), charlie);
2485 env(pay(issuer, owner, asset(args.initialIOU)));
2486 env(rate(issuer, args.transferRate));
2489 auto const vaultAccount =
2491 return Account(
"vault", env.le(keylet)->at(sfAccount));
2494 return env.le(keylet)->at(sfShareMPTID);
2510 Account
const& owner,
2511 Account
const& issuer,
2517 testcase(
"IOU cannot use different asset");
2520 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2526 auto tx = [&, account = vaultAccount(keylet)]() {
2528 jv[jss::Account] = issuer.human();
2530 auto& ja = jv[jss::LimitAmount] =
2532 ja[jss::issuer] =
toBase58(account);
2534 jv[jss::TransactionType] = jss::TrustSet;
2543 auto tx = vault.deposit(
2544 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2550 auto tx = vault.withdraw(
2551 {.depositor = issuer, .id = keylet.
key, .amount = foo(20)});
2556 env(vault.del({.owner = owner, .id = keylet.key}));
2562 Account
const& owner,
2563 Account
const& issuer,
2564 Account
const& charlie,
2569 testcase(
"IOU frozen trust line to vault account");
2571 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2576 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2579 Asset const share =
Asset(issuanceId(keylet));
2582 auto trustSet = [&, account = vaultAccount(keylet)]() {
2584 jv[jss::Account] = issuer.human();
2586 auto& ja = jv[jss::LimitAmount] =
2588 ja[jss::issuer] =
toBase58(account);
2590 jv[jss::TransactionType] = jss::TrustSet;
2602 auto tx = vault.deposit(
2603 {.depositor = owner,
2605 .amount = asset(80)});
2610 auto tx = vault.withdraw(
2611 {.depositor = owner,
2613 .amount = asset(100)});
2617 tx[sfDestination] = charlie.human();
2624 auto tx = vault.clawback(
2628 .amount = asset(50)});
2639 {.depositor = owner,
2641 .amount = share(50'000'000)}));
2643 env(vault.del({.owner = owner, .id = keylet.key}));
2650 Account
const& owner,
2651 Account
const& issuer,
2652 Account
const& charlie,
2657 testcase(
"IOU transfer fees not applied");
2660 vault.create({.owner = owner, .asset = asset});
2665 {.depositor = owner,
2667 .amount = asset(100)}));
2671 Asset const share =
Asset(issuanceId(keylet));
2674 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2676 env.balance(vaultAccount(keylet), issue) == asset(100));
2679 auto tx = vault.clawback(
2683 .amount = asset(50)});
2689 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
2691 env.balance(vaultAccount(keylet), issue) == asset(50));
2694 {.depositor = owner,
2696 .amount = share(20'000'000)}));
2699 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2701 env.balance(vaultAccount(keylet), issue) == asset(30));
2704 auto tx = vault.withdraw(
2705 {.depositor = owner,
2707 .amount = share(30'000'000)});
2708 tx[sfDestination] = charlie.human();
2713 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
2714 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
2716 env.balance(vaultAccount(keylet), issue) == asset(0));
2718 env(vault.del({.owner = owner, .id = keylet.key}));
2721 CaseArgs{.transferRate = 1.25});
2725 Account
const& owner,
2726 Account
const& issuer,
2727 Account
const& charlie,
2732 testcase(
"IOU frozen trust line to depositor");
2734 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2739 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2744 auto tx = vault.withdraw(
2745 {.depositor = owner,
2747 .amount = asset(10)});
2748 tx[sfDestination] = charlie.human();
2751 env(withdrawToCharlie);
2758 auto const withdraw = vault.withdraw(
2759 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
2768 auto tx = vault.deposit(
2769 {.depositor = owner,
2771 .amount = asset(10)});
2777 auto tx = vault.clawback(
2781 .amount = asset(0)});
2786 env(vault.del({.owner = owner, .id = keylet.key}));
2792 Account
const& owner,
2793 Account
const& issuer,
2794 Account
const& charlie,
2799 testcase(
"IOU no trust line to 3rd party");
2801 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2806 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
2809 Account
const erin{
"erin"};
2810 env.fund(XRP(1000), erin);
2815 auto tx = vault.withdraw(
2816 {.depositor = owner,
2818 .amount = asset(10)});
2819 tx[sfDestination] = erin.human();
2827 Account
const& owner,
2828 Account
const& issuer,
2829 Account
const& charlie,
2834 testcase(
"IOU no trust line to depositor");
2836 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
2841 env.trust(asset(0), owner);
2845 {.depositor = owner, .id = keylet.key, .amount = asset(200)}));
2850 BEAST_EXPECT(trustline ==
nullptr);
2854 auto tx = vault.withdraw(
2855 {.depositor = owner,
2857 .amount = asset(10)});
2866 Account
const& owner,
2867 Account
const& issuer,
2868 Account
const& charlie,
2869 auto const& vaultAccount,
2873 testcase(
"IOU calculation rounding");
2876 vault.create({.owner = owner, .asset = asset});
2881 auto const startingOwnerBalance = env.balance(owner, asset);
2883 (startingOwnerBalance.value() ==
2891 {.depositor = owner,
2893 .amount = asset(100)}));
2895 auto const tx1 = vault.deposit(
2896 {.depositor = owner,
2898 .amount = asset(
Number(375, -2))});
2899 for (
auto i = 0; i < 5; ++i)
2906 STAmount const xfer{asset, 1185, -1};
2908 env.balance(owner, asset) ==
2909 startingOwnerBalance.value() - xfer);
2911 env.balance(vaultAccount(keylet), asset) == xfer);
2913 auto const vault = env.le(keylet);
2914 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
2915 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
2922 {.depositor = owner,
2924 .amount = asset(Number(1000 + 37 * 5, -1))}));
2928 env.balance(owner, asset) ==
2929 startingOwnerBalance.value());
2931 env.balance(vaultAccount(keylet), asset) ==
2933 auto const vault = env.le(keylet);
2934 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
2935 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
2938 env(vault.del({.owner = owner, .id = keylet.key}));
2941 {.initialIOU =
Number(11875, -2)});
2944 Env env{*
this, testable_amendments()};
2946 env.current()->fees().accountReserve(0).drops() /
2948 env.current()->fees().increment.drops() /
2955 Account
const& owner,
2956 Account
const& issuer,
2957 Account
const& charlie,
2962 testcase(
"IOU no trust line to depositor no reserve");
2964 vault.create({.owner = owner, .asset = asset});
2970 env.trust(asset(0), owner);
2974 {.depositor = owner,
2976 .amount = asset(200)}));
2981 BEAST_EXPECT(trustline ==
nullptr);
2984 tx = vault.withdraw(
2985 {.depositor = owner,
2987 .amount = asset(10)});
2991 env(pay(charlie, owner, XRP(incReserve)));
2998 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
3003 Account
const& owner,
3004 Account
const& issuer,
3005 Account
const& charlie,
3010 testcase(
"IOU no reserve for share MPToken");
3012 vault.create({.owner = owner, .asset = asset});
3016 env(pay(owner, charlie, asset(100)));
3020 env(ticket::create(charlie, 2));
3025 {.depositor = charlie,
3027 .amount = asset(100)});
3031 env(pay(issuer, charlie, XRP(incReserve)));
3038 CaseArgs{.initialXRP = acctReserve + incReserve * 4 - 1});
3042 Account
const& owner,
3043 Account
const& issuer,
3044 Account
const& charlie,
3049 testcase(
"IOU frozen trust line to 3rd party");
3051 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3056 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3061 auto tx = vault.withdraw(
3062 {.depositor = owner,
3064 .amount = asset(10)});
3065 tx[sfDestination] = charlie.human();
3068 env(withdrawToCharlie);
3071 env(trust(issuer, asset(0), charlie,
tfSetFreeze));
3075 auto const withdraw = vault.withdraw(
3076 {.depositor = owner, .id = keylet.
key, .amount = asset(10)});
3088 .amount = asset(0)}));
3091 env(vault.del({.owner = owner, .id = keylet.key}));
3097 Account
const& owner,
3098 Account
const& issuer,
3099 Account
const& charlie,
3106 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3111 {.depositor = owner, .id = keylet.key, .amount = asset(100)}));
3119 auto tx = vault.withdraw(
3120 {.depositor = owner,
3122 .amount = asset(10)});
3126 tx[sfDestination] = charlie.human();
3132 {.depositor = owner,
3134 .amount = asset(10)});
3144 .amount = asset(0)}));
3147 env(vault.del({.owner = owner, .id = keylet.key}));
3155 using namespace test::jtx;
3159 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3160 Account issuer{
"issuer"};
3161 Account owner{
"owner"};
3162 Account depositor{
"depositor"};
3163 Account charlie{
"charlie"};
3164 Account pdOwner{
"pdOwner"};
3165 Account credIssuer1{
"credIssuer1"};
3166 Account credIssuer2{
"credIssuer2"};
3184 env.trust(asset(1000), owner);
3185 env(pay(issuer, owner, asset(500)));
3186 env.trust(asset(1000), depositor);
3187 env(pay(issuer, depositor, asset(500)));
3188 env.trust(asset(1000), charlie);
3189 env(pay(issuer, charlie, asset(5)));
3192 auto [tx, keylet] = vault.create(
3196 BEAST_EXPECT(env.le(keylet));
3199 testcase(
"private vault owner can deposit");
3200 auto tx = vault.deposit(
3201 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
3206 testcase(
"private vault depositor not authorized yet");
3207 auto tx = vault.deposit(
3208 {.depositor = depositor,
3210 .amount = asset(50)});
3215 testcase(
"private vault cannot set non-existing domain");
3216 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3222 testcase(
"private vault set domainId");
3225 pdomain::Credentials
const credentials1{
3226 {.issuer = credIssuer1, .credType = credType}};
3228 env(pdomain::setTx(pdOwner, credentials1));
3229 auto const domainId1 = [&]() {
3231 return pdomain::getNewDomain(env.meta());
3234 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3245 pdomain::Credentials
const credentials{
3246 {.issuer = credIssuer1, .credType = credType},
3247 {.issuer = credIssuer2, .credType = credType}};
3249 env(pdomain::setTx(pdOwner, credentials));
3250 auto const domainId = [&]() {
3252 return pdomain::getNewDomain(env.meta());
3255 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3261 tx = vault.set({.owner = owner, .id = keylet.
key});
3269 testcase(
"private vault depositor still not authorized");
3270 auto tx = vault.deposit(
3271 {.depositor = depositor,
3273 .amount = asset(50)});
3278 auto const credKeylet =
3279 credentials::keylet(depositor, credIssuer1, credType);
3281 testcase(
"private vault depositor now authorized");
3282 env(credentials::create(depositor, credIssuer1, credType));
3283 env(credentials::accept(depositor, credIssuer1, credType));
3284 env(credentials::create(charlie, credIssuer1, credType));
3287 auto credSle = env.le(credKeylet);
3288 BEAST_EXPECT(credSle !=
nullptr);
3290 auto tx = vault.deposit(
3291 {.depositor = depositor,
3293 .amount = asset(50)});
3298 {.depositor = charlie, .id = keylet.
key, .amount = asset(50)});
3304 testcase(
"private vault depositor lost authorization");
3305 env(credentials::deleteCred(
3306 credIssuer1, depositor, credIssuer1, credType));
3307 env(credentials::deleteCred(
3308 credIssuer1, charlie, credIssuer1, credType));
3310 auto credSle = env.le(credKeylet);
3311 BEAST_EXPECT(credSle ==
nullptr);
3313 auto tx = vault.deposit(
3314 {.depositor = depositor,
3316 .amount = asset(50)});
3321 auto const shares = [&env, keylet = keylet,
this]() ->
Asset {
3322 auto const vault = env.le(keylet);
3323 BEAST_EXPECT(vault !=
nullptr);
3324 return MPTIssue(vault->at(sfShareMPTID));
3328 testcase(
"private vault expired authorization");
3329 uint32_t
const closeTime = env.current()
3331 .parentCloseTime.time_since_epoch()
3335 credentials::create(depositor, credIssuer2, credType);
3336 tx0[sfExpiration] = closeTime + 20;
3338 tx0 = credentials::create(charlie, credIssuer2, credType);
3339 tx0[sfExpiration] = closeTime + 20;
3343 env(credentials::accept(depositor, credIssuer2, credType));
3344 env(credentials::accept(charlie, credIssuer2, credType));
3349 auto tx1 = vault.deposit(
3350 {.depositor = depositor,
3352 .amount = asset(50)});
3358 BEAST_EXPECT(env.le(tokenKeylet) !=
nullptr);
3367 auto const credsKeylet =
3368 credentials::keylet(depositor, credIssuer2, credType);
3369 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3371 auto tx2 = vault.deposit(
3372 {.depositor = depositor,
3374 .amount = asset(1)});
3378 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3382 auto const credsKeylet =
3383 credentials::keylet(charlie, credIssuer2, credType);
3384 BEAST_EXPECT(env.le(credsKeylet) !=
nullptr);
3387 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3389 auto tx3 = vault.deposit(
3390 {.depositor = charlie,
3392 .amount = asset(2)});
3396 BEAST_EXPECT(env.le(credsKeylet) ==
nullptr);
3397 BEAST_EXPECT(env.le(tokenKeylet) ==
nullptr);
3402 testcase(
"private vault reset domainId");
3403 auto tx = vault.set({.owner = owner, .id = keylet.
key});
3404 tx[sfDomainID] =
"0";
3409 {.depositor = depositor,
3411 .amount = asset(50)});
3415 tx = vault.withdraw(
3416 {.depositor = depositor,
3418 .amount = asset(50)});
3422 tx = vault.clawback(
3425 .holder = depositor,
3426 .amount = asset(0)});
3429 tx = vault.clawback(
3433 .amount = asset(0)});
3582 using namespace test::jtx;
3586 Account
const& owner;
3587 Account
const& issuer;
3588 Account
const& depositor;
3589 Account
const& vaultAccount;
3599 auto testCase = [&,
this](
3602 Env env{*
this, testable_amendments() | featureSingleAssetVault};
3603 Account
const owner{
"owner"};
3604 Account
const issuer{
"issuer"};
3605 Account
const depositor{
"depositor"};
3607 env.fund(XRP(1000), issuer, owner, depositor);
3612 env.trust(asset(1000), owner);
3613 env.trust(asset(1000), depositor);
3614 env(pay(issuer, owner, asset(200)));
3615 env(pay(issuer, depositor, asset(200)));
3618 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
3619 tx[sfScale] = scale;
3622 auto const [vaultAccount, issuanceId] =
3624 auto const vault = env.le(keylet);
3626 Account(
"vault", vault->at(sfAccount)),
3627 vault->at(sfShareMPTID)};
3630 env.memoize(vaultAccount);
3634 return env.app().openLedger().modify(
3638 if (!BEAST_EXPECT(vault !=
nullptr))
3640 auto shares = sb.
peek(
3642 if (!BEAST_EXPECT(shares !=
nullptr))
3644 if (fn(*vault, *shares))
3659 .depositor = depositor,
3660 .vaultAccount = vaultAccount,
3670 testCase(18, [&,
this](Env& env, Data d) {
3671 testcase(
"Scale deposit overflow on first deposit");
3672 auto tx = d.vault.deposit(
3673 {.depositor = d.depositor,
3675 .amount = d.asset(10)});
3680 testCase(18, [&,
this](Env& env, Data d) {
3681 testcase(
"Scale deposit overflow on second deposit");
3684 auto tx = d.vault.deposit(
3685 {.depositor = d.depositor,
3687 .amount = d.asset(5)});
3693 auto tx = d.vault.deposit(
3694 {.depositor = d.depositor,
3696 .amount = d.asset(10)});
3702 testCase(18, [&,
this](Env& env, Data d) {
3703 testcase(
"Scale deposit overflow on total shares");
3706 auto tx = d.vault.deposit(
3707 {.depositor = d.depositor,
3709 .amount = d.asset(5)});
3715 auto tx = d.vault.deposit(
3716 {.depositor = d.depositor,
3718 .amount = d.asset(5)});
3724 testCase(1, [&,
this](Env& env, Data d) {
3727 auto const start = env.balance(d.depositor, d.assets).number();
3728 auto tx = d.vault.deposit(
3729 {.depositor = d.depositor,
3731 .amount = d.asset(1)});
3734 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
3736 env.balance(d.depositor, d.assets) ==
3740 testCase(1, [&,
this](Env& env, Data d) {
3741 testcase(
"Scale deposit insignificant amount");
3743 auto tx = d.vault.deposit(
3744 {.depositor = d.depositor,
3750 testCase(1, [&,
this](Env& env, Data d) {
3751 testcase(
"Scale deposit exact, using full precision");
3753 auto const start = env.balance(d.depositor, d.assets).number();
3754 auto tx = d.vault.deposit(
3755 {.depositor = d.depositor,
3760 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
3762 env.balance(d.depositor, d.assets) ==
3766 testCase(1, [&,
this](Env& env, Data d) {
3767 testcase(
"Scale deposit exact, truncating from .5");
3769 auto const start = env.balance(d.depositor, d.assets).number();
3773 auto tx = d.vault.deposit(
3774 {.depositor = d.depositor,
3779 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3781 env.balance(d.depositor, d.assets) ==
3786 auto tx = d.vault.deposit(
3787 {.depositor = d.depositor,
3792 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
3794 env.balance(d.depositor, d.assets) ==
3799 auto tx = d.vault.deposit(
3800 {.depositor = d.depositor,
3805 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
3807 env.balance(d.depositor, d.assets) ==
3812 testCase(1, [&,
this](Env& env, Data d) {
3813 testcase(
"Scale deposit exact, truncating from .01");
3815 auto const start = env.balance(d.depositor, d.assets).number();
3817 auto tx = d.vault.deposit(
3818 {.depositor = d.depositor,
3823 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3825 env.balance(d.depositor, d.assets) ==
3830 auto tx = d.vault.deposit(
3831 {.depositor = d.depositor,
3836 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3838 env.balance(d.depositor, d.assets) ==
3843 testCase(1, [&,
this](Env& env, Data d) {
3844 testcase(
"Scale deposit exact, truncating from .99");
3846 auto const start = env.balance(d.depositor, d.assets).number();
3848 auto tx = d.vault.deposit(
3849 {.depositor = d.depositor,
3854 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
3856 env.balance(d.depositor, d.assets) ==
3861 auto tx = d.vault.deposit(
3862 {.depositor = d.depositor,
3867 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
3869 env.balance(d.depositor, d.assets) ==
3874 testCase(1, [&,
this](Env& env, Data d) {
3876 auto const start = env.balance(d.depositor, d.assets).number();
3877 auto tx = d.vault.deposit(
3878 {.depositor = d.depositor,
3883 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
3885 env.balance(d.depositor, d.assets) ==
3888 env.balance(d.vaultAccount, d.assets) ==
3891 env.balance(d.vaultAccount, d.shares) ==
3900 auto const start = env.balance(d.depositor, d.assets).number();
3901 auto tx = d.vault.withdraw(
3902 {.depositor = d.depositor,
3908 env.balance(d.depositor, d.shares) == d.share(900));
3910 env.balance(d.depositor, d.assets) ==
3913 env.balance(d.vaultAccount, d.assets) ==
3916 env.balance(d.vaultAccount, d.shares) ==
3921 testcase(
"Scale redeem with rounding");
3926 auto const start = env.balance(d.depositor, d.assets).number();
3927 d.peek([](
SLE& vault,
auto&) ->
bool {
3928 vault[sfAssetsAvailable] =
Number(1);
3936 auto tx = d.vault.withdraw(
3937 {.depositor = d.depositor,
3943 env.balance(d.depositor, d.shares) == d.share(900 - 25));
3945 env.balance(d.depositor, d.assets) ==
3948 env.balance(d.vaultAccount, d.assets) ==
3951 env.balance(d.vaultAccount, d.shares) ==
3961 auto const start = env.balance(d.depositor, d.assets).number();
3963 tx = d.vault.withdraw(
3964 {.depositor = d.depositor,
3970 env.balance(d.depositor, d.shares) == d.share(875 - 21));
3972 env.balance(d.depositor, d.assets) ==
3975 env.balance(d.vaultAccount, d.assets) ==
3978 env.balance(d.vaultAccount, d.shares) ==
3984 auto const rest = env.balance(d.depositor, d.shares).number();
3986 tx = d.vault.withdraw(
3987 {.depositor = d.depositor,
3989 .amount =
STAmount(d.share, rest)});
3992 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
3994 env.balance(d.vaultAccount, d.assets).number() == 0);
3996 env.balance(d.vaultAccount, d.shares).number() == 0);
4000 testCase(18, [&,
this](Env& env, Data d) {
4001 testcase(
"Scale withdraw overflow");
4004 auto tx = d.vault.deposit(
4005 {.depositor = d.depositor,
4007 .amount = d.asset(5)});
4013 auto tx = d.vault.withdraw(
4014 {.depositor = d.depositor,
4022 testCase(1, [&,
this](Env& env, Data d) {
4024 auto const start = env.balance(d.depositor, d.assets).number();
4025 auto tx = d.vault.deposit(
4026 {.depositor = d.depositor,
4031 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4033 env.balance(d.depositor, d.assets) ==
4036 env.balance(d.vaultAccount, d.assets) ==
4039 env.balance(d.vaultAccount, d.shares) ==
4051 auto const start = env.balance(d.depositor, d.assets).number();
4052 auto tx = d.vault.withdraw(
4053 {.depositor = d.depositor,
4059 env.balance(d.depositor, d.shares) == d.share(900));
4061 env.balance(d.depositor, d.assets) ==
4064 env.balance(d.vaultAccount, d.assets) ==
4067 env.balance(d.vaultAccount, d.shares) ==
4072 testcase(
"Scale withdraw insignificant amount");
4073 auto tx = d.vault.withdraw(
4074 {.depositor = d.depositor,
4081 testcase(
"Scale withdraw with rounding assets");
4089 auto const start = env.balance(d.depositor, d.assets).number();
4090 d.peek([](
SLE& vault,
auto&) ->
bool {
4091 vault[sfAssetsAvailable] =
Number(1);
4099 auto tx = d.vault.withdraw(
4100 {.depositor = d.depositor,
4106 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4108 env.balance(d.depositor, d.assets) ==
4111 env.balance(d.vaultAccount, d.assets) ==
4114 env.balance(d.vaultAccount, d.shares) ==
4119 testcase(
"Scale withdraw with rounding shares up");
4127 auto const start = env.balance(d.depositor, d.assets).number();
4128 auto tx = d.vault.withdraw(
4129 {.depositor = d.depositor,
4135 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4137 env.balance(d.depositor, d.assets) ==
4140 env.balance(d.vaultAccount, d.assets) ==
4143 env.balance(d.vaultAccount, d.shares) ==
4148 testcase(
"Scale withdraw with rounding shares down");
4156 auto const start = env.balance(d.depositor, d.assets).number();
4157 auto tx = d.vault.withdraw(
4158 {.depositor = d.depositor,
4164 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4166 env.balance(d.depositor, d.assets) ==
4169 env.balance(d.vaultAccount, d.assets) ==
4172 env.balance(d.vaultAccount, d.shares) ==
4177 testcase(
"Scale withdraw tiny amount");
4179 auto const start = env.balance(d.depositor, d.assets).number();
4180 auto tx = d.vault.withdraw(
4181 {.depositor = d.depositor,
4187 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4189 env.balance(d.depositor, d.assets) ==
4192 env.balance(d.vaultAccount, d.assets) ==
4195 env.balance(d.vaultAccount, d.shares) ==
4202 env.balance(d.vaultAccount, d.assets).number();
4204 tx = d.vault.withdraw(
4205 {.depositor = d.depositor,
4207 .amount =
STAmount(d.asset, rest)});
4210 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4212 env.balance(d.vaultAccount, d.assets).number() == 0);
4214 env.balance(d.vaultAccount, d.shares).number() == 0);
4218 testCase(18, [&,
this](Env& env, Data d) {
4219 testcase(
"Scale clawback overflow");
4222 auto tx = d.vault.deposit(
4223 {.depositor = d.depositor,
4225 .amount = d.asset(5)});
4231 auto tx = d.vault.clawback(
4232 {.issuer = d.issuer,
4234 .holder = d.depositor,
4241 testCase(1, [&,
this](Env& env, Data d) {
4243 auto const start = env.balance(d.depositor, d.assets).number();
4244 auto tx = d.vault.deposit(
4245 {.depositor = d.depositor,
4250 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
4252 env.balance(d.depositor, d.assets) ==
4255 env.balance(d.vaultAccount, d.assets) ==
4258 env.balance(d.vaultAccount, d.shares) ==
4269 auto const start = env.balance(d.depositor, d.assets).number();
4270 auto tx = d.vault.clawback(
4271 {.issuer = d.issuer,
4273 .holder = d.depositor,
4278 env.balance(d.depositor, d.shares) == d.share(900));
4280 env.balance(d.depositor, d.assets) ==
4283 env.balance(d.vaultAccount, d.assets) ==
4286 env.balance(d.vaultAccount, d.shares) ==
4291 testcase(
"Scale clawback insignificant amount");
4292 auto tx = d.vault.clawback(
4293 {.issuer = d.issuer,
4295 .holder = d.depositor,
4301 testcase(
"Scale clawback with rounding assets");
4309 auto const start = env.balance(d.depositor, d.assets).number();
4310 auto tx = d.vault.clawback(
4311 {.issuer = d.issuer,
4313 .holder = d.depositor,
4318 env.balance(d.depositor, d.shares) == d.share(900 - 25));
4320 env.balance(d.depositor, d.assets) ==
4323 env.balance(d.vaultAccount, d.assets) ==
4326 env.balance(d.vaultAccount, d.shares) ==
4331 testcase(
"Scale clawback with rounding shares up");
4339 auto const start = env.balance(d.depositor, d.assets).number();
4340 auto tx = d.vault.clawback(
4341 {.issuer = d.issuer,
4343 .holder = d.depositor,
4348 env.balance(d.depositor, d.shares) == d.share(875 - 38));
4350 env.balance(d.depositor, d.assets) ==
4353 env.balance(d.vaultAccount, d.assets) ==
4356 env.balance(d.vaultAccount, d.shares) ==
4361 testcase(
"Scale clawback with rounding shares down");
4369 auto const start = env.balance(d.depositor, d.assets).number();
4370 auto tx = d.vault.clawback(
4371 {.issuer = d.issuer,
4373 .holder = d.depositor,
4378 env.balance(d.depositor, d.shares) == d.share(837 - 37));
4380 env.balance(d.depositor, d.assets) ==
4383 env.balance(d.vaultAccount, d.assets) ==
4386 env.balance(d.vaultAccount, d.shares) ==
4391 testcase(
"Scale clawback tiny amount");
4393 auto const start = env.balance(d.depositor, d.assets).number();
4394 auto tx = d.vault.clawback(
4395 {.issuer = d.issuer,
4397 .holder = d.depositor,
4402 env.balance(d.depositor, d.shares) == d.share(800 - 1));
4404 env.balance(d.depositor, d.assets) ==
4407 env.balance(d.vaultAccount, d.assets) ==
4410 env.balance(d.vaultAccount, d.shares) ==
4417 env.balance(d.vaultAccount, d.assets).number();
4418 d.peek([](
SLE& vault,
auto&) ->
bool {
4419 vault[sfAssetsAvailable] =
Number(5);
4427 tx = d.vault.clawback(
4428 {.issuer = d.issuer,
4430 .holder = d.depositor,
4431 .amount =
STAmount(d.asset, rest)});
4434 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
4436 env.balance(d.vaultAccount, d.assets).number() == 0);
4438 env.balance(d.vaultAccount, d.shares).number() == 0);
4446 using namespace test::jtx;
4449 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4450 Account
const owner{
"owner"};
4451 Account
const issuer{
"issuer"};
4453 env.fund(XRP(1000), issuer, owner);
4457 env.trust(asset(1000), owner);
4458 env(pay(issuer, owner, asset(200)));
4461 auto const sequence = env.seq(owner);
4462 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
4468 auto tx1 = vault.deposit(
4469 {.depositor = owner, .id = keylet.
key, .amount = asset(50)});
4472 auto tx2 = vault.set({.owner = owner, .id = keylet.
key});
4473 tx2[sfAssetsMaximum] = asset(1000).number();
4478 auto const sleVault = [&env, keylet = keylet,
this]() {
4479 auto const vault = env.le(keylet);
4480 BEAST_EXPECT(vault !=
nullptr);
4484 auto const check = [&, keylet = keylet, sle = sleVault,
this](
4487 BEAST_EXPECT(vault.isObject());
4489 constexpr auto checkString =
4491 return node.isMember(field.fieldName) &&
4492 node[field.fieldName].isString() &&
4493 node[field.fieldName] == v;
4495 constexpr auto checkObject =
4497 return node.isMember(field.fieldName) &&
4498 node[field.fieldName].isObject() &&
4499 node[field.fieldName] == v;
4501 constexpr auto checkInt =
4502 [](
auto& node,
SField const& field,
int v) ->
bool {
4503 return node.isMember(field.fieldName) &&
4504 ((node[field.fieldName].isInt() &&
4505 node[field.fieldName] ==
Json::Int(v)) ||
4506 (node[field.fieldName].isUInt() &&
4510 BEAST_EXPECT(vault[
"LedgerEntryType"].asString() ==
"Vault");
4511 BEAST_EXPECT(vault[jss::index].asString() ==
strHex(keylet.
key));
4512 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
4516 checkString(vault, sfAccount,
toBase58(sle->at(sfAccount))));
4518 checkObject(vault, sfAsset,
to_json(sle->at(sfAsset))));
4519 BEAST_EXPECT(checkString(vault, sfAssetsAvailable,
"50"));
4520 BEAST_EXPECT(checkString(vault, sfAssetsMaximum,
"1000"));
4521 BEAST_EXPECT(checkString(vault, sfAssetsTotal,
"50"));
4522 BEAST_EXPECT(checkString(vault, sfLossUnrealized,
"0"));
4524 auto const strShareID =
strHex(sle->at(sfShareMPTID));
4525 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
4526 BEAST_EXPECT(checkString(vault, sfOwner,
toBase58(owner.id())));
4527 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
4528 BEAST_EXPECT(checkInt(
4531 if (issuance.isObject())
4534 issuance[
"LedgerEntryType"].asString() ==
4537 issuance[jss::mpt_issuance_id].asString() == strShareID);
4538 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
4539 BEAST_EXPECT(checkInt(
4544 checkString(issuance, sfOutstandingAmount,
"50000000"));
4549 testcase(
"RPC ledger_entry selected by key");
4551 jvParams[jss::ledger_index] = jss::validated;
4552 jvParams[jss::vault] =
strHex(keylet.
key);
4553 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4555 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4556 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4557 check(jvVault[jss::result][jss::node]);
4561 testcase(
"RPC ledger_entry selected by owner and seq");
4563 jvParams[jss::ledger_index] = jss::validated;
4564 jvParams[jss::vault][jss::owner] = owner.human();
4565 jvParams[jss::vault][jss::seq] = sequence;
4566 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4568 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
4569 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
4570 check(jvVault[jss::result][jss::node]);
4574 testcase(
"RPC ledger_entry cannot find vault by key");
4576 jvParams[jss::ledger_index] = jss::validated;
4578 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4580 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4584 testcase(
"RPC ledger_entry cannot find vault by owner and seq");
4586 jvParams[jss::ledger_index] = jss::validated;
4587 jvParams[jss::vault][jss::owner] = issuer.human();
4588 jvParams[jss::vault][jss::seq] = 1'000'000;
4589 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4591 jvVault[jss::result][jss::error].asString() ==
"entryNotFound");
4595 testcase(
"RPC ledger_entry malformed key");
4597 jvParams[jss::ledger_index] = jss::validated;
4598 jvParams[jss::vault] = 42;
4599 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4601 jvVault[jss::result][jss::error].asString() ==
4602 "malformedRequest");
4606 testcase(
"RPC ledger_entry malformed owner");
4608 jvParams[jss::ledger_index] = jss::validated;
4609 jvParams[jss::vault][jss::owner] = 42;
4610 jvParams[jss::vault][jss::seq] = sequence;
4611 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4613 jvVault[jss::result][jss::error].asString() ==
4618 testcase(
"RPC ledger_entry malformed seq");
4620 jvParams[jss::ledger_index] = jss::validated;
4621 jvParams[jss::vault][jss::owner] = issuer.human();
4622 jvParams[jss::vault][jss::seq] =
"foo";
4623 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4625 jvVault[jss::result][jss::error].asString() ==
4626 "malformedRequest");
4630 testcase(
"RPC ledger_entry negative seq");
4632 jvParams[jss::ledger_index] = jss::validated;
4633 jvParams[jss::vault][jss::owner] = issuer.human();
4634 jvParams[jss::vault][jss::seq] = -1;
4635 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4637 jvVault[jss::result][jss::error].asString() ==
4638 "malformedRequest");
4642 testcase(
"RPC ledger_entry oversized seq");
4644 jvParams[jss::ledger_index] = jss::validated;
4645 jvParams[jss::vault][jss::owner] = issuer.human();
4646 jvParams[jss::vault][jss::seq] = 1e20;
4647 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4649 jvVault[jss::result][jss::error].asString() ==
4650 "malformedRequest");
4654 testcase(
"RPC ledger_entry bool seq");
4656 jvParams[jss::ledger_index] = jss::validated;
4657 jvParams[jss::vault][jss::owner] = issuer.human();
4658 jvParams[jss::vault][jss::seq] =
true;
4659 auto jvVault = env.rpc(
"json",
"ledger_entry",
to_string(jvParams));
4661 jvVault[jss::result][jss::error].asString() ==
4662 "malformedRequest");
4669 jvParams[jss::account] = owner.human();
4670 jvParams[jss::type] = jss::vault;
4672 "json",
"account_objects",
to_string(jvParams))[jss::result];
4674 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
4675 check(jv[jss::account_objects][0u]);
4682 jvParams[jss::ledger_index] = jss::validated;
4683 jvParams[jss::binary] =
false;
4684 jvParams[jss::type] = jss::vault;
4686 env.rpc(
"json",
"ledger_data",
to_string(jvParams));
4687 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
4688 check(jv[jss::result][jss::state][0u]);
4692 testcase(
"RPC vault_info command line");
4694 env.rpc(
"vault_info",
strHex(keylet.
key),
"validated");
4696 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4697 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4699 jv[jss::result][jss::vault],
4700 jv[jss::result][jss::vault][jss::shares]);
4706 jvParams[jss::ledger_index] = jss::validated;
4707 jvParams[jss::vault_id] =
strHex(keylet.
key);
4708 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4710 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4711 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4713 jv[jss::result][jss::vault],
4714 jv[jss::result][jss::vault][jss::shares]);
4718 testcase(
"RPC vault_info invalid vault_id");
4720 jvParams[jss::ledger_index] = jss::validated;
4721 jvParams[jss::vault_id] =
"foobar";
4722 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4724 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4728 testcase(
"RPC vault_info json invalid index");
4730 jvParams[jss::ledger_index] = jss::validated;
4731 jvParams[jss::vault_id] = 0;
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 by owner and sequence");
4740 jvParams[jss::ledger_index] = jss::validated;
4741 jvParams[jss::owner] = owner.human();
4742 jvParams[jss::seq] = sequence;
4743 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4745 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
4746 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
4748 jv[jss::result][jss::vault],
4749 jv[jss::result][jss::vault][jss::shares]);
4753 testcase(
"RPC vault_info json malformed sequence");
4755 jvParams[jss::ledger_index] = jss::validated;
4756 jvParams[jss::owner] = owner.human();
4757 jvParams[jss::seq] =
"foobar";
4758 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4760 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4764 testcase(
"RPC vault_info json invalid sequence");
4766 jvParams[jss::ledger_index] = jss::validated;
4767 jvParams[jss::owner] = owner.human();
4768 jvParams[jss::seq] = 0;
4769 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4771 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4775 testcase(
"RPC vault_info json negative sequence");
4777 jvParams[jss::ledger_index] = jss::validated;
4778 jvParams[jss::owner] = owner.human();
4779 jvParams[jss::seq] = -1;
4780 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4782 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4786 testcase(
"RPC vault_info json oversized sequence");
4788 jvParams[jss::ledger_index] = jss::validated;
4789 jvParams[jss::owner] = owner.human();
4790 jvParams[jss::seq] = 1e20;
4791 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4793 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4797 testcase(
"RPC vault_info json bool sequence");
4799 jvParams[jss::ledger_index] = jss::validated;
4800 jvParams[jss::owner] = owner.human();
4801 jvParams[jss::seq] =
true;
4802 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4804 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4808 testcase(
"RPC vault_info json malformed owner");
4810 jvParams[jss::ledger_index] = jss::validated;
4811 jvParams[jss::owner] =
"foobar";
4812 jvParams[jss::seq] = sequence;
4813 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4815 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4819 testcase(
"RPC vault_info json invalid combination only owner");
4821 jvParams[jss::ledger_index] = jss::validated;
4822 jvParams[jss::owner] = owner.human();
4823 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4825 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4829 testcase(
"RPC vault_info json invalid combination only seq");
4831 jvParams[jss::ledger_index] = jss::validated;
4832 jvParams[jss::seq] = sequence;
4833 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4835 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4839 testcase(
"RPC vault_info json invalid combination seq vault_id");
4841 jvParams[jss::ledger_index] = jss::validated;
4842 jvParams[jss::vault_id] =
strHex(keylet.
key);
4843 jvParams[jss::seq] = sequence;
4844 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4846 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4850 testcase(
"RPC vault_info json invalid combination owner vault_id");
4852 jvParams[jss::ledger_index] = jss::validated;
4853 jvParams[jss::vault_id] =
strHex(keylet.
key);
4854 jvParams[jss::owner] = owner.human();
4855 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4857 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4862 "RPC vault_info json invalid combination owner seq "
4865 jvParams[jss::ledger_index] = jss::validated;
4866 jvParams[jss::vault_id] =
strHex(keylet.
key);
4867 jvParams[jss::seq] = sequence;
4868 jvParams[jss::owner] = owner.human();
4869 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4871 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4875 testcase(
"RPC vault_info json no input");
4877 jvParams[jss::ledger_index] = jss::validated;
4878 auto jv = env.rpc(
"json",
"vault_info",
to_string(jvParams));
4880 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4884 testcase(
"RPC vault_info command line invalid index");
4885 Json::Value jv = env.rpc(
"vault_info",
"foobar",
"validated");
4886 BEAST_EXPECT(jv[jss::error].asString() ==
"invalidParams");
4890 testcase(
"RPC vault_info command line invalid index");
4891 Json::Value jv = env.rpc(
"vault_info",
"0",
"validated");
4893 jv[jss::result][jss::error].asString() ==
"malformedRequest");
4897 testcase(
"RPC vault_info command line invalid index");
4901 jv[jss::result][jss::error].asString() ==
"entryNotFound");
4905 testcase(
"RPC vault_info command line invalid ledger");
4908 jv[jss::result][jss::error].asString() ==
"lgrNotFound");
4915 using namespace test::jtx;
4917 Env env(*
this, testable_amendments());
4918 Account alice{
"alice"};
4920 Account carol{
"carol"};
4927 auto const xrpBalance =
4931 if (BEAST_EXPECT(sle !=
nullptr))
4932 return sle->getFieldAmount(sfBalance).xrp().drops();
4936 auto testCase = [&,
this](
auto test, CaseArgs args = {}) {
4937 Env env{*
this, testable_amendments() | featureSingleAssetVault};
4942 env.fund(XRP(10000), alice);
4943 env.fund(XRP(20000), bob);
4944 env.fund(XRP(30000), carol);
4958 test(env, vault, args.asset);
4961 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4962 testcase(
"delegated vault creation");
4963 auto startBalance = xrpBalance(env, carol);
4964 if (!BEAST_EXPECT(startBalance.has_value()))
4967 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4968 env(tx, delegate::as(alice));
4970 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
4973 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
4974 testcase(
"delegated deposit and withdrawal");
4975 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
4979 auto const amount = 1513;
4980 auto const baseFee = env.current()->fees().base;
4982 auto startBalance = xrpBalance(env, carol);
4983 if (!BEAST_EXPECT(startBalance.has_value()))
4987 {.depositor = carol,
4989 .amount = asset(amount)});
4990 env(tx, delegate::as(alice));
4992 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
4994 tx = vault.withdraw(
4995 {.depositor = carol,
4997 .amount = asset(amount - 1)});
4998 env(tx, delegate::as(alice));
5000 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
5002 tx = vault.withdraw(
5003 {.depositor = carol, .id = keylet.
key, .amount = asset(1)});
5006 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5009 testCase([&,
this](Env& env, Vault& vault,
PrettyAsset const& asset) {
5010 testcase(
"delegated withdrawal same as base fee and deletion");
5011 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
5015 auto const amount = 25537;
5016 auto const baseFee = env.current()->fees().base;
5018 auto startBalance = xrpBalance(env, carol);
5019 if (!BEAST_EXPECT(startBalance.has_value()))
5023 {.depositor = carol,
5025 .amount = asset(amount)});
5029 xrpBalance(env, carol) == *startBalance - amount - baseFee);
5031 tx = vault.withdraw(
5032 {.depositor = carol,
5034 .amount = asset(baseFee)});
5035 env(tx, delegate::as(alice));
5037 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
5039 tx = vault.withdraw(
5040 {.depositor = carol,
5042 .amount = asset(amount - baseFee)});
5043 env(tx, delegate::as(alice));
5045 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
5047 tx = vault.del({.owner = carol, .id = keylet.
key});
5048 env(tx, delegate::as(alice));